[U-Boot] [PATCH] ARM: tegra210: implement PLLE init procedure from TRM

Stephen Warren swarren at wwwdotorg.org
Tue Oct 6 00:58:52 CEST 2015


From: Stephen Warren <swarren at nvidia.com>

Implement the procedure that the TRM mandates to initialize PLLREFE and
PLLE. This makes the PLL actually lock.

Note that this section of the TRM is being cleaned up to remove some
confusion. The set of register accesses in this patch should be final,
although the step numbers/descriptions might still change.

Signed-off-by: Stephen Warren <swarren at nvidia.com>
---
 arch/arm/mach-tegra/tegra210/clock.c | 179 ++++++++++++++++++++++++++---------
 1 file changed, 132 insertions(+), 47 deletions(-)

diff --git a/arch/arm/mach-tegra/tegra210/clock.c b/arch/arm/mach-tegra/tegra210/clock.c
index 6d75d371cb03..df92bdce889e 100644
--- a/arch/arm/mach-tegra/tegra210/clock.c
+++ b/arch/arm/mach-tegra/tegra210/clock.c
@@ -8,6 +8,7 @@
 /* Tegra210 Clock control functions */
 
 #include <common.h>
+#include <errno.h>
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/sysctr.h>
@@ -1030,6 +1031,59 @@ void arch_timer_init(void)
 	debug("%s: TSC CNTCR = 0x%08X\n", __func__, val);
 }
 
+#define PLLREFE_MISC			0x4c8
+#define  PLLREFE_MISC_LOCK		BIT(27)
+#define  PLLREFE_MISC_IDDQ		BIT(24)
+
+#define PLLREFE_BASE			0x4c4
+#define  PLLREFE_BASE_BYPASS		BIT(31)
+#define  PLLREFE_BASE_ENABLE		BIT(30)
+#define  PLLREFE_BASE_REF_DIS		BIT(29)
+#define  PLLREFE_BASE_KCP(kcp)		(((kcp) & 0x3) << 27)
+#define  PLLREFE_BASE_KVCO		BIT(26)
+#define  PLLREFE_BASE_DIVP(p)		(((p) & 0x1f) << 16)
+#define  PLLREFE_BASE_DIVN(n)		(((n) & 0xff) << 8)
+#define  PLLREFE_BASE_DIVM(m)		(((m) & 0xff) << 0)
+
+static int tegra_pllref_enable(void)
+{
+	u32 value;
+	unsigned long start;
+
+	/*
+	 * This sequence comes from Tegra X1 TRM section "Cold Boot, with no
+	 * Recovery Mode or Boot from USB", sub-section "PLLREFE".
+	 */
+
+	value = readl(NV_PA_CLK_RST_BASE + PLLREFE_MISC);
+	value &= ~PLLREFE_MISC_IDDQ;
+	writel(value, NV_PA_CLK_RST_BASE + PLLREFE_MISC);
+
+	udelay(5);
+
+	value = PLLREFE_BASE_ENABLE |
+		PLLREFE_BASE_KCP(0) |
+		PLLREFE_BASE_DIVP(0) |
+		PLLREFE_BASE_DIVN(0x41) |
+		PLLREFE_BASE_DIVM(4);
+	writel(value, NV_PA_CLK_RST_BASE + PLLREFE_BASE);
+
+	debug("waiting for pllrefe lock\n");
+	start = get_timer(0);
+	while (get_timer(start) < 250) {
+		value = readl(NV_PA_CLK_RST_BASE + PLLREFE_MISC);
+		if (value & PLLREFE_MISC_LOCK)
+			break;
+	}
+	if (!(value & PLLREFE_MISC_LOCK)) {
+		debug("  timeout\n");
+		return -ETIMEDOUT;
+	}
+	debug("  done\n");
+
+	return 0;
+}
+
 #define PLLE_SS_CNTL 0x68
 #define  PLLE_SS_CNTL_SSCINCINTR(x) (((x) & 0x3f) << 24)
 #define  PLLE_SS_CNTL_SSCINC(x) (((x) & 0xff) << 16)
@@ -1041,100 +1095,131 @@ void arch_timer_init(void)
 #define  PLLE_SS_CNTL_SSCMAX(x) (((x) & 0x1ff) << 0)
 
 #define PLLE_BASE 0x0e8
-#define  PLLE_BASE_ENABLE (1 << 30)
-#define  PLLE_BASE_LOCK_OVERRIDE (1 << 29)
-#define  PLLE_BASE_PLDIV_CML(x) (((x) & 0xf) << 24)
+#define  PLLE_BASE_ENABLE (1 << 31)
+#define  PLLE_BASE_PLDIV_CML(x) (((x) & 0x1f) << 24)
 #define  PLLE_BASE_NDIV(x) (((x) & 0xff) << 8)
 #define  PLLE_BASE_MDIV(x) (((x) & 0xff) << 0)
 
 #define PLLE_MISC 0x0ec
 #define  PLLE_MISC_IDDQ_SWCTL (1 << 14)
-#define  PLLE_MISC_IDDQ_OVERRIDE (1 << 13)
-#define  PLLE_MISC_LOCK_ENABLE (1 << 9)
-#define  PLLE_MISC_PTS (1 << 8)
-#define  PLLE_MISC_VREG_BG_CTRL(x) (((x) & 0x3) << 4)
+#define  PLLE_MISC_IDDQ_OVERRIDE_VALUE (1 << 13)
+#define  PLLE_MISC_LOCK (1 << 11)
+#define  PLLE_MISC_KCP(x) (((x) & 0x3) << 6)
 #define  PLLE_MISC_VREG_CTRL(x) (((x) & 0x3) << 2)
+#define  PLLE_MISC_KVCO (1 << 0)
 
 #define PLLE_AUX 0x48c
+#define  PLLE_AUX_SS_SEQ_INCLUDE (1 << 31)
+#define  PLLE_AUX_REF_SEL_PLLREFE (1 << 28)
 #define  PLLE_AUX_SEQ_ENABLE (1 << 24)
+#define  PLLE_AUX_SS_SWCTL (1 << 6)
 #define  PLLE_AUX_ENABLE_SWCTL (1 << 4)
+#define  PLLE_AUX_USE_LOCKDET (1 << 3)
 
 int tegra_plle_enable(void)
 {
-	unsigned int m = 1, n = 200, cpcon = 13;
 	u32 value;
+	unsigned long start;
 
-	value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE);
-	value &= ~PLLE_BASE_LOCK_OVERRIDE;
-	writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE);
+	/* PLLREF feeds PLLE */
+	tegra_pllref_enable();
+
+	/*
+	 * This sequence comes from Tegra X1 TRM section "Cold Boot, with no
+	 * Recovery Mode or Boot from USB", sub-section "PLLEs".
+	 */
+
+	/* 1. Select XTAL as the source */
 
 	value = readl(NV_PA_CLK_RST_BASE + PLLE_AUX);
-	value |= PLLE_AUX_ENABLE_SWCTL;
-	value &= ~PLLE_AUX_SEQ_ENABLE;
+	value &= ~PLLE_AUX_REF_SEL_PLLREFE;
 	writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX);
 
-	udelay(1);
-
 	value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC);
-	value |= PLLE_MISC_IDDQ_SWCTL;
-	value &= ~PLLE_MISC_IDDQ_OVERRIDE;
-	value |= PLLE_MISC_LOCK_ENABLE;
-	value |= PLLE_MISC_PTS;
-	value |= PLLE_MISC_VREG_BG_CTRL(3);
-	value |= PLLE_MISC_VREG_CTRL(2);
+	value &= ~PLLE_MISC_IDDQ_OVERRIDE_VALUE;
 	writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC);
 
+	/* 2. Wait 5 us */
 	udelay(5);
 
-	value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
-	value |= PLLE_SS_CNTL_SSCBYP | PLLE_SS_CNTL_INTERP_RESET |
-		 PLLE_SS_CNTL_BYPASS_SS;
-	writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
+	/*
+	 * 3. Program the following registers to generate a low jitter 100MHz
+	 * clock.
+	 */
 
 	value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE);
-	value &= ~PLLE_BASE_PLDIV_CML(0xf);
+	value &= ~PLLE_BASE_PLDIV_CML(0x1f);
 	value &= ~PLLE_BASE_NDIV(0xff);
 	value &= ~PLLE_BASE_MDIV(0xff);
-	value |= PLLE_BASE_PLDIV_CML(cpcon);
-	value |= PLLE_BASE_NDIV(n);
-	value |= PLLE_BASE_MDIV(m);
+	value |= PLLE_BASE_PLDIV_CML(0xe);
+	value |= PLLE_BASE_NDIV(0x7d);
+	value |= PLLE_BASE_MDIV(2);
 	writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE);
 
-	udelay(1);
+	value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC);
+	value &= ~PLLE_MISC_KCP(3);
+	value &= ~PLLE_MISC_VREG_CTRL(3);
+	value &= ~PLLE_MISC_KVCO;
+	writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC);
 
 	value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE);
 	value |= PLLE_BASE_ENABLE;
 	writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE);
 
-	/* wait for lock */
-	udelay(300);
-
-	value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
-	value &= ~PLLE_SS_CNTL_SSCINVERT;
-	value &= ~PLLE_SS_CNTL_SSCCENTER;
-
-	value &= ~PLLE_SS_CNTL_SSCINCINTR(0x3f);
-	value &= ~PLLE_SS_CNTL_SSCINC(0xff);
-	value &= ~PLLE_SS_CNTL_SSCMAX(0x1ff);
+	/* 4. Wait for LOCK */
 
-	value |= PLLE_SS_CNTL_SSCINCINTR(0x20);
-	value |= PLLE_SS_CNTL_SSCINC(0x01);
-	value |= PLLE_SS_CNTL_SSCMAX(0x25);
+	debug("waiting for plle lock\n");
+	start = get_timer(0);
+	while (get_timer(start) < 250) {
+		value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC);
+		if (value & PLLE_MISC_LOCK)
+			break;
+	}
+	if (!(value & PLLE_MISC_LOCK)) {
+		debug("  timeout\n");
+		return -ETIMEDOUT;
+	}
+	debug("  done\n");
 
-	writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
+	/* 5. Enable SSA */
 
 	value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
-	value &= ~PLLE_SS_CNTL_SSCBYP;
+	value &= ~PLLE_SS_CNTL_SSCINC(0xff);
+	value |= PLLE_SS_CNTL_SSCINC(1);
+	value &= ~PLLE_SS_CNTL_SSCINCINTR(0x3f);
+	value |= PLLE_SS_CNTL_SSCINCINTR(0x23);
+	value &= ~PLLE_SS_CNTL_SSCMAX(0x1fff);
+	value |= PLLE_SS_CNTL_SSCMAX(0x21);
+	value &= ~PLLE_SS_CNTL_SSCINVERT;
+	value &= ~PLLE_SS_CNTL_SSCCENTER;
 	value &= ~PLLE_SS_CNTL_BYPASS_SS;
+	value &= ~PLLE_SS_CNTL_SSCBYP;
 	writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
 
-	udelay(1);
+	/* 6. Wait 300 ns */
 
-	value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
+	udelay(1);
 	value &= ~PLLE_SS_CNTL_INTERP_RESET;
 	writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL);
 
+	/* 7. Enable HW power sequencer for PLLE */
+
+	value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC);
+	value &= ~PLLE_MISC_IDDQ_SWCTL;
+	writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC);
+
+	value = readl(NV_PA_CLK_RST_BASE + PLLE_AUX);
+	value &= ~PLLE_AUX_SS_SWCTL;
+	value &= ~PLLE_AUX_ENABLE_SWCTL;
+	value |= PLLE_AUX_SS_SEQ_INCLUDE;
+	value |= PLLE_AUX_USE_LOCKDET;
+	writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX);
+
+	/* 8. Wait 1 us */
+
 	udelay(1);
+	value |= PLLE_AUX_SEQ_ENABLE;
+	writel(value, NV_PA_CLK_RST_BASE + PLLE_AUX);
 
 	return 0;
 }
-- 
1.9.1



More information about the U-Boot mailing list