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

Reply via email to