This patch gets phy phandle from dt-binding, and power cycle/re-configure phy whilst changing card clock.
Signed-off-by: Ziyuan Xu <xzy...@rock-chips.com> --- drivers/mmc/rockchip_sdhci.c | 147 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index 562fb35..5b6b262 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -6,6 +6,7 @@ * SPDX-License-Identifier: GPL-2.0+ */ +#include <asm/arch/hardware.h> #include <common.h> #include <dm.h> #include <dt-structs.h> @@ -28,11 +29,151 @@ struct rockchip_sdhc_plat { struct mmc mmc; }; +struct rockchip_emmc_phy { + u32 emmcphy_con[7]; + u32 reserved; + u32 emmcphy_status; +}; + struct rockchip_sdhc { struct sdhci_host host; void *base; + struct rockchip_emmc_phy *phy; }; +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 + +#define PHYCTRL_DLLRDY_MASK 0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE 0x1 + +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 + +#define KHz (1000) +#define MHz (1000 * KHz) + +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) +{ + u32 caldone, dllrdy, freqsel; + uint start; + + writel(RK_CLRSETBITS(7 << 4, 0), &phy->emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 11, 1 << 11), &phy->emmcphy_con[0]); + writel(RK_CLRSETBITS(0xf << 7, 4 << 7), &phy->emmcphy_con[0]); + + /* + * According to the user manual, calpad calibration + * cycle takes more than 2us without the minimal recommended + * value, so we may need a little margin here + */ + udelay(3); + writel(RK_CLRSETBITS(1, 1), &phy->emmcphy_con[6]); + + /* + * According to the user manual, it asks driver to + * wait 5us for calpad busy trimming + */ + udelay(5); + caldone = readl(&phy->emmcphy_status); + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; + if (caldone != PHYCTRL_CALDONE_DONE) { + debug("%s: caldone timeout.\n", __func__); + return; + } + + /* Set the frequency of the DLL operation */ + if (clock < 75 * MHz) + freqsel = PHYCTRL_FREQSEL_50M; + else if (clock < 125 * MHz) + freqsel = PHYCTRL_FREQSEL_100M; + else if (clock < 175 * MHz) + freqsel = PHYCTRL_FREQSEL_150M; + else + freqsel = PHYCTRL_FREQSEL_200M; + + /* Set the frequency of the DLL operation */ + writel(RK_CLRSETBITS(3 << 12, freqsel << 12), &phy->emmcphy_con[0]); + writel(RK_CLRSETBITS(1 << 1, 1 << 1), &phy->emmcphy_con[6]); + + start = get_timer(0); + + do { + udelay(1); + dllrdy = readl(&phy->emmcphy_status); + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (dllrdy == PHYCTRL_DLLRDY_DONE) + break; + } while (get_timer(start) < 50000); + + if (dllrdy != PHYCTRL_DLLRDY_DONE) + debug("%s: dllrdy timeout.\n", __func__); +} + +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) +{ + writel(RK_CLRSETBITS(1, 0), &phy->emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 1, 0), &phy->emmcphy_con[6]); +} + +static int arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct rockchip_sdhc *priv = + container_of(host, struct rockchip_sdhc, host); + int cycle_phy = host->clock != clock && + clock > EMMC_MIN_FREQ; + + if (cycle_phy) + rk3399_emmc_phy_power_off(priv->phy); + + sdhci_set_clock(host, clock); + + if (cycle_phy) + rk3399_emmc_phy_power_on(priv->phy, clock); + + return 0; +} + +static struct sdhci_ops arasan_sdhci_ops = { + .set_clock = arasan_sdhci_set_clock, +}; + +static int arasan_get_phy(struct udevice *dev) +{ + struct rockchip_sdhc *priv = dev_get_priv(dev); + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + priv->phy = (struct rockchip_emmc_phy *)0xff77f780; +#else + int phy_node, grf_node; + fdt_addr_t grf_base, grf_phy_offset; + + phy_node = fdtdec_lookup_phandle(gd->fdt_blob, + dev_of_offset(dev), "phys"); + if (phy_node <= 0) { + debug("Not found emmc phy device\n"); + return -ENODEV; + } + + grf_node = fdt_parent_offset(gd->fdt_blob, phy_node); + if (grf_node <= 0) { + debug("Not found usb phy device\n"); + return -ENODEV; + } + + grf_base = fdtdec_get_addr(gd->fdt_blob, grf_node, "reg"); + grf_phy_offset = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, + grf_node, phy_node, "reg", 0, NULL, false); + + priv->phy = (struct rockchip_emmc_phy *)(grf_base + grf_phy_offset); +#endif + return 0; +} + static int arasan_sdhci_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); @@ -77,6 +218,12 @@ static int arasan_sdhci_probe(struct udevice *dev) printf("%s fail to get clk\n", __func__); } + ret = arasan_get_phy(dev); + if (ret) + return ret; + + host->ops = &arasan_sdhci_ops; + host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD; host->max_clk = max_frequency; -- 2.7.4 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot