diff options
-rw-r--r-- | arch/arm/mach-tegra/tegra210/clock.c | 179 |
1 files changed, 132 insertions, 47 deletions
diff --git a/arch/arm/mach-tegra/tegra210/clock.c b/arch/arm/mach-tegra/tegra210/clock.c index 6d75d37..df92bdc 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; } |