ARM: tegra210: implement PLLE init procedure from TRM
authorStephen Warren <swarren@nvidia.com>
Mon, 5 Oct 2015 22:58:52 +0000 (16:58 -0600)
committerTom Warren <twarren@nvidia.com>
Thu, 12 Nov 2015 16:21:04 +0000 (09:21 -0700)
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@nvidia.com>
Signed-off-by: Tom Warren <twarren@nvidia.com>
arch/arm/mach-tegra/tegra210/clock.c

index 6d75d371cb03cb6290cad1d971b41ca8d2d2c7e5..df92bdce889ec4930cc0808e36bb4b85ca1e1cd1 100644 (file)
@@ -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;
 }