summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZiyuan Xu <xzy.xu@rock-chips.com>2017-05-15 09:07:23 +0300
committerKever Yang <kever.yang@rock-chips.com>2017-07-18 17:08:31 +0300
commitb7917ff10887a417a34cabece2ea1825fcae467d (patch)
tree922389082c60bb647457013eb5d157768c52210d
parent85c817741d2d88fc8257587658ba323ccfa1ccb8 (diff)
downloadu-boot-b7917ff10887a417a34cabece2ea1825fcae467d.tar.xz
mmc: sdhci: rockchip: add phy support
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@rock-chips.com>
-rw-r--r--drivers/mmc/rockchip_sdhci.c147
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c
index f4050b1c6f..fd3bb1042a 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);
@@ -75,6 +216,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;