[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