[U-Boot] [PATCH 29/33] mmc: sdhci: rockchip: add phy support

Ziyuan Xu xzy.xu at rock-chips.com
Mon May 15 06:07:23 UTC 2017


This patch gets phy phandle from dt-binding, and power
cycle/re-configure phy whilst changing card clock.

Signed-off-by: Ziyuan Xu <xzy.xu at 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




More information about the U-Boot mailing list