[U-Boot] [PATCH 1/2] Tegra: clocks: Add 38.4MHz OSC support for T210 use

Added 38.4MHz/48MHz entries to pll_x_table for CPU PLL. Needs to be measured - should be close to 700MHz (1.4G/2).
Note that some freqs aren't in the PLLU table in T210 TRM (13, 26MHz), so I used the 12MHz table entry for them. They shouldn't be selected since they're not viable T210 OSC freqs.
Since there are now 2 new OSC defines, all tables (pll_x_table, PLLU) had to increase by two entries, but since 38.4/48MHz are not viable osc freqs on T20/30/114, etc, they're just set to 0.
Signed-off-by: Tom Warren twarren@nvidia.com --- arch/arm/include/asm/arch-tegra/clock.h | 2 ++ arch/arm/mach-tegra/clock.c | 13 +++++++++---- arch/arm/mach-tegra/cpu.c | 12 ++++++++++++ arch/arm/mach-tegra/tegra210/clock.c | 8 ++++++-- drivers/usb/host/ehci-tegra.c | 18 +++++++++++++----- 5 files changed, 42 insertions(+), 11 deletions(-)
diff --git a/arch/arm/include/asm/arch-tegra/clock.h b/arch/arm/include/asm/arch-tegra/clock.h index f9dd3c8..2274b14 100644 --- a/arch/arm/include/asm/arch-tegra/clock.h +++ b/arch/arm/include/asm/arch-tegra/clock.h @@ -16,6 +16,8 @@ enum clock_osc_freq { CLOCK_OSC_FREQ_19_2, CLOCK_OSC_FREQ_12_0, CLOCK_OSC_FREQ_26_0, + CLOCK_OSC_FREQ_38_4, + CLOCK_OSC_FREQ_48_0,
CLOCK_OSC_FREQ_COUNT, }; diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 5d968d8..d0eebd2 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -44,6 +44,8 @@ static unsigned osc_freq[CLOCK_OSC_FREQ_COUNT] = { 19200000, 12000000, 26000000, + 38400000, + 48000000, };
/* return 1 if a peripheral ID is in range */ @@ -620,17 +622,20 @@ int clock_verify(void)
void clock_init(void) { + pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL); pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY); pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH); - pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL); + pll_rate[CLOCK_ID_USB] = clock_get_rate(CLOCK_ID_USB); pll_rate[CLOCK_ID_DISPLAY] = clock_get_rate(CLOCK_ID_DISPLAY); - pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC); - pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; pll_rate[CLOCK_ID_XCPU] = clock_get_rate(CLOCK_ID_XCPU); + pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; + pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC); + debug("Osc = %d\n", pll_rate[CLOCK_ID_OSC]); + debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]); debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]); debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]); - debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]); + debug("PLLU = %d\n", pll_rate[CLOCK_ID_USB]); debug("PLLD = %d\n", pll_rate[CLOCK_ID_DISPLAY]); debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]); } diff --git a/arch/arm/mach-tegra/cpu.c b/arch/arm/mach-tegra/cpu.c index f7d45e8..b9391d6 100644 --- a/arch/arm/mach-tegra/cpu.c +++ b/arch/arm/mach-tegra/cpu.c @@ -67,6 +67,8 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = { { .n = 625, .m = 12, .p = 0, .cpcon = 8 }, /* OSC: 19.2 MHz */ { .n = 1000, .m = 12, .p = 0, .cpcon = 12 }, /* OSC: 12.0 MHz */ { .n = 1000, .m = 26, .p = 0, .cpcon = 12 }, /* OSC: 26.0 MHz */ + { .n = 0, .m = 0, .p = 0, .cpcon = 0 }, /* OSC: 38.4 MHz (N/A) */ + { .n = 0, .m = 0, .p = 0, .cpcon = 0 }, /* OSC: 48.0 MHz (N/A) */ }, /* * T25: 1.2 GHz @@ -83,6 +85,8 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = { { .n = 750, .m = 12, .p = 0, .cpcon = 8 }, /* OSC: 19.2 MHz */ { .n = 600, .m = 6, .p = 0, .cpcon = 12 }, /* OSC: 12.0 MHz */ { .n = 600, .m = 13, .p = 0, .cpcon = 12 }, /* OSC: 26.0 MHz */ + { .n = 0, .m = 0, .p = 0, .cpcon = 0 }, /* OSC: 38.4 MHz (N/A) */ + { .n = 0, .m = 0, .p = 0, .cpcon = 0 }, /* OSC: 48.0 MHz (N/A) */ }, /* * T30: 600 MHz @@ -99,6 +103,8 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = { { .n = 500, .m = 16, .p = 0, .cpcon = 8 }, /* OSC: 19.2 MHz */ { .n = 600, .m = 12, .p = 0, .cpcon = 8 }, /* OSC: 12.0 MHz */ { .n = 600, .m = 26, .p = 0, .cpcon = 8 }, /* OSC: 26.0 MHz */ + { .n = 0, .m = 0, .p = 0, .cpcon = 0 }, /* OSC: 38.4 MHz (N/A) */ + { .n = 0, .m = 0, .p = 0, .cpcon = 0 }, /* OSC: 48.0 MHz (N/A) */ }, /* * T114: 700 MHz @@ -114,6 +120,8 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = { { .n = 73, .m = 1, .p = 1 }, /* OSC: 19.2 MHz */ { .n = 116, .m = 1, .p = 1 }, /* OSC: 12.0 MHz */ { .n = 108, .m = 2, .p = 1 }, /* OSC: 26.0 MHz */ + { .n = 0, .m = 0, .p = 0 }, /* OSC: 38.4 MHz (N/A) */ + { .n = 0, .m = 0, .p = 0 }, /* OSC: 48.0 MHz (N/A) */ },
/* @@ -130,6 +138,8 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = { { .n = 73, .m = 1, .p = 1 }, /* OSC: 19.2 MHz */ { .n = 116, .m = 1, .p = 1 }, /* OSC: 12.0 MHz */ { .n = 108, .m = 2, .p = 1 }, /* OSC: 26.0 MHz */ + { .n = 0, .m = 0, .p = 0 }, /* OSC: 38.4 MHz (N/A) */ + { .n = 0, .m = 0, .p = 0 }, /* OSC: 48.0 MHz (N/A) */ },
/* @@ -146,6 +156,8 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = { { .n = 73, .m = 1, .p = 1 }, /* OSC: 19.2 MHz = 700.8 MHz*/ { .n = 116, .m = 1, .p = 1 }, /* OSC: 12.0 MHz = 696 MHz*/ { .n = 108, .m = 2, .p = 1 }, /* OSC: 26.0 MHz = 702 MHz*/ + { .n = 36, .m = 1, .p = 1 }, /* OSC: 38.4 MHz = 691.2 MHz */ + { .n = 58, .m = 2, .p = 1 }, /* OSC: 48.0 MHz = 696 MHz */ }, };
diff --git a/arch/arm/mach-tegra/tegra210/clock.c b/arch/arm/mach-tegra/tegra210/clock.c index 4e7d793..7ac2a06 100644 --- a/arch/arm/mach-tegra/tegra210/clock.c +++ b/arch/arm/mach-tegra/tegra210/clock.c @@ -649,8 +649,8 @@ enum clock_osc_freq clock_get_osc_freq(void) */ if (reg == 5) { debug("OSC_FREQ is 38.4MHz (%d) ...\n", reg); - /* Map it to 19.2MHz for now. 38.4MHz OSC support TBD */ - return 1; + /* Map it to the 5th CLOCK_OSC_ enum, i.e. 4 */ + return 4; }
/* @@ -930,6 +930,10 @@ void clock_early_init(void) clock_set_rate(CLOCK_ID_CGENERAL, 125, 4, 0, 0); clock_set_rate(CLOCK_ID_DISPLAY, 96, 2, 0, 12); break; + case CLOCK_OSC_FREQ_38_4: + clock_set_rate(CLOCK_ID_CGENERAL, 125, 8, 0, 0); + clock_set_rate(CLOCK_ID_DISPLAY, 96, 4, 0, 0); + break; default: /* * These are not supported. It is too early to print a diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 0edd557..4a4f559 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -131,7 +131,9 @@ static const unsigned T20_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { { 0x3C0, 0x0D, 0x00, 0xC, 0, 0x02, 0x33, 0x05, 0x7F, 0x7EF4, 5 }, { 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x06, 0xBB, 0xBB80, 7 }, { 0x3C0, 0x0C, 0x00, 0xC, 0, 0x02, 0x2F, 0x04, 0x76, 0x7530, 5 }, - { 0x3C0, 0x1A, 0x00, 0xC, 0, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } + { 0x3C0, 0x1A, 0x00, 0xC, 0, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 }, + { 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 }, + { 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 } };
static const unsigned T30_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { @@ -139,7 +141,9 @@ static const unsigned T30_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { { 0x3C0, 0x0D, 0x00, 0xC, 1, 0x02, 0x33, 0x09, 0x7F, 0x7EF4, 5 }, { 0x0C8, 0x04, 0x00, 0x3, 0, 0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 7 }, { 0x3C0, 0x0C, 0x00, 0xC, 1, 0x02, 0x2F, 0x08, 0x76, 0x7530, 5 }, - { 0x3C0, 0x1A, 0x00, 0xC, 1, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } + { 0x3C0, 0x1A, 0x00, 0xC, 1, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 }, + { 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 }, + { 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 } };
static const unsigned T114_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { @@ -147,16 +151,20 @@ static const unsigned T114_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { { 0x3C0, 0x0D, 0x00, 0xC, 2, 0x02, 0x33, 0x09, 0x7F, 0x7EF4, 6 }, { 0x0C8, 0x04, 0x00, 0x3, 2, 0x03, 0x4B, 0x0C, 0xBB, 0xBB80, 8 }, { 0x3C0, 0x0C, 0x00, 0xC, 2, 0x02, 0x2F, 0x08, 0x76, 0x7530, 5 }, - { 0x3C0, 0x1A, 0x00, 0xC, 2, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 0xB } + { 0x3C0, 0x1A, 0x00, 0xC, 2, 0x04, 0x66, 0x09, 0xFE, 0xFDE8, 11 }, + { 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 }, + { 0x000, 0x00, 0x00, 0x0, 0, 0x00, 0x00, 0x00, 0x00, 0x0000, 0 } };
/* NOTE: 13/26MHz settings are N/A for T210, so dupe 12MHz settings for now */ static const unsigned T210_usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { /* DivN, DivM, DivP, KCP, KVCO, Delays Debounce, Bias */ - { 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 30000, 5 }, + { 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 32500, 5 }, { 0x019, 0x01, 0x01, 0x0, 0, 0x03, 0x4B, 0x0C, 0xBB, 48000, 8 }, { 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 30000, 5 }, - { 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 30000, 5 }, + { 0x028, 0x01, 0x01, 0x0, 0, 0x02, 0x2F, 0x08, 0x76, 65000, 5 }, + { 0x019, 0x02, 0x01, 0x0, 0, 0x05, 0x96, 0x18, 0x177, 96000, 15 }, + { 0x028, 0x04, 0x01, 0x0, 0, 0x04, 0x66, 0x09, 0xFE, 120000, 20 } };
/* UTMIP Idle Wait Delay */

Added PLL variables (dividers mask/shift, lock enable/detect, etc.) to new pllinfo struct for each Soc/PLL. PLLA/C/D/E/M/P/U/X.
Used pllinfo struct in all clock functions, validated on T210. Should be equivalent to prior code on T124/114/30/20 but needs test.
Corrections to divm mask vs shift and T20/30 divN masks thanks to Marcel Ziswiler.
Signed-off-by: Tom Warren twarren@nvidia.com --- arch/arm/include/asm/arch-tegra/clk_rst.h | 32 +------ arch/arm/include/asm/arch-tegra/clock.h | 21 +++++ arch/arm/include/asm/arch-tegra210/clock-tables.h | 1 + arch/arm/mach-tegra/clock.c | 108 ++++++++-------------- arch/arm/mach-tegra/cpu.c | 18 ++-- arch/arm/mach-tegra/tegra114/clock.c | 57 +++++++++--- arch/arm/mach-tegra/tegra114/cpu.c | 39 +++----- arch/arm/mach-tegra/tegra124/clock.c | 44 ++++++++- arch/arm/mach-tegra/tegra124/cpu.c | 31 +++---- arch/arm/mach-tegra/tegra20/clock.c | 32 +++++++ arch/arm/mach-tegra/tegra210/clock.c | 31 ++++++- arch/arm/mach-tegra/tegra30/clock.c | 45 ++++++--- 12 files changed, 285 insertions(+), 174 deletions(-)
diff --git a/arch/arm/include/asm/arch-tegra/clk_rst.h b/arch/arm/include/asm/arch-tegra/clk_rst.h index f690260..ee9436e 100644 --- a/arch/arm/include/asm/arch-tegra/clk_rst.h +++ b/arch/arm/include/asm/arch-tegra/clk_rst.h @@ -249,17 +249,6 @@ struct clk_rst_ctlr { #define PLL_LOCK_SHIFT 27 #define PLL_LOCK_MASK (1U << PLL_LOCK_SHIFT)
-#define PLL_DIVP_SHIFT 20 -#define PLL_DIVP_MASK (7U << PLL_DIVP_SHIFT) -/* Special case for T210 PLLU DIVP */ -#define PLLU_DIVP_SHIFT 16 - -#define PLL_DIVN_SHIFT 8 -#define PLL_DIVN_MASK (0x3ffU << PLL_DIVN_SHIFT) - -#define PLL_DIVM_SHIFT 0 -#define PLL_DIVM_MASK (0x1f << PLL_DIVM_SHIFT) - /* CLK_RST_CONTROLLER_PLLx_OUTx_0 */ #define PLL_OUT_RSTN (1 << 0) #define PLL_OUT_CLKEN (1 << 1) @@ -272,24 +261,6 @@ struct clk_rst_ctlr { #define PLL_DCCON_SHIFT 20 #define PLL_DCCON_MASK (1U << PLL_DCCON_SHIFT)
-#define PLL_LOCK_ENABLE_SHIFT 18 -#define PLL_LOCK_ENABLE_MASK (1U << PLL_LOCK_ENABLE_SHIFT) - -#define PLL_CPCON_SHIFT 8 -#define PLL_CPCON_MASK (15U << PLL_CPCON_SHIFT) - -#define PLL_LFCON_SHIFT 4 -#define PLL_LFCON_MASK (15U << PLL_LFCON_SHIFT) - -/* CPCON/LFCON replaced by KCP/KVCO in T210 PLLU */ -#define PLLU_KVCO_SHIFT 24 -#define PLLU_KVCO_MASK (3U << PLLU_KVCO_SHIFT) -#define PLLU_KCP_SHIFT 25 -#define PLLU_KCP_MASK (1U << PLLU_KCP_SHIFT) - -#define PLLU_VCO_FREQ_SHIFT 20 -#define PLLU_VCO_FREQ_MASK (1U << PLLU_VCO_FREQ_SHIFT) - #define PLLP_OUT1_OVR (1 << 2) #define PLLP_OUT2_OVR (1 << 18) #define PLLP_OUT3_OVR (1 << 2) @@ -475,4 +446,7 @@ enum { #define PLLDP_SS_CFG_UNDOCUMENTED (1 << 24) #define PLLDP_SS_CFG_DITHER (1 << 28)
+/* CLK_RST_PLLD_MISC */ +#define PLLD_CLKENABLE 30 + #endif /* _TEGRA_CLK_RST_H_ */ diff --git a/arch/arm/include/asm/arch-tegra/clock.h b/arch/arm/include/asm/arch-tegra/clock.h index 2274b14..d570d7f 100644 --- a/arch/arm/include/asm/arch-tegra/clock.h +++ b/arch/arm/include/asm/arch-tegra/clock.h @@ -338,6 +338,27 @@ void arch_timer_init(void);
void tegra30_set_up_pllp(void);
+/* Number of PLL-based clocks (i.e. not OSC or 32KHz) */ +#define CLOCK_ID_PLL_COUNT (CLOCK_ID_COUNT - 2) + +struct clk_pll_info { + u32 m_shift:5; /* DIVM_SHIFT */ + u32 n_shift:5; /* DIVN_SHIFT */ + u32 p_shift:5; /* DIVP_SHIFT */ + u32 kcp_shift:5; /* KCP/cpcon SHIFT */ + u32 kvco_shift:5; /* KVCO/lfcon SHIFT */ + u32 lock_ena:6; /* LOCK_ENABLE/EN_LOCKDET shift */ + u32 rsvd:1; + u32 m_mask:10; /* DIVM_MASK */ + u32 n_mask:12; /* DIVN_MASK */ + u32 p_mask:10; /* DIVP_MASK or VCO_MASK */ + u32 kcp_mask:10; /* KCP/CPCON MASK */ + u32 kvco_mask:10; /* KVCO/LFCON MASK */ + u32 lock_det:6; /* LOCK_DETECT/LOCKED shift */ + u32 rsvd2:6; +}; +extern struct clk_pll_info tegra_pll_info_table[CLOCK_ID_PLL_COUNT]; + /** * Enable output clock for external peripherals * diff --git a/arch/arm/include/asm/arch-tegra210/clock-tables.h b/arch/arm/include/asm/arch-tegra210/clock-tables.h index b62e070..175040d 100644 --- a/arch/arm/include/asm/arch-tegra210/clock-tables.h +++ b/arch/arm/include/asm/arch-tegra210/clock-tables.h @@ -25,6 +25,7 @@ enum clock_id { CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE, CLOCK_ID_EPCI, CLOCK_ID_SFROM32KHZ, + CLOCK_ID_DP,
/* These are the base clocks (inputs to the Tegra SoC) */ CLOCK_ID_32KHZ, diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index d0eebd2..3b2b4ff 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -101,6 +101,7 @@ int clock_ll_read_pll(enum clock_id clkid, u32 *divm, u32 *divn, u32 *divp, u32 *cpcon, u32 *lfcon) { struct clk_pll *pll = get_pll(clkid); + struct clk_pll_info *pllinfo = &tegra_pll_info_table[clkid]; u32 data;
assert(clkid != CLOCK_ID_USB); @@ -109,17 +110,14 @@ int clock_ll_read_pll(enum clock_id clkid, u32 *divm, u32 *divn, if (!clock_id_is_pll(clkid) || clkid == CLOCK_ID_USB) return -1; data = readl(&pll->pll_base); - *divm = (data & PLL_DIVM_MASK) >> PLL_DIVM_SHIFT; - *divn = (data & PLL_DIVN_MASK) >> PLL_DIVN_SHIFT; - *divp = (data & PLL_DIVP_MASK) >> PLL_DIVP_SHIFT; + *divm = (data >> pllinfo->m_shift) & pllinfo->m_mask; + *divn = (data >> pllinfo->n_shift) & pllinfo->n_mask; + *divp = (data >> pllinfo->p_shift) & pllinfo->p_mask; data = readl(&pll->pll_misc); - *cpcon = (data & PLL_CPCON_MASK) >> PLL_CPCON_SHIFT; - *lfcon = (data & PLL_LFCON_MASK) >> PLL_LFCON_SHIFT; -#if defined(CONFIG_TEGRA210) - /* T210 PLLU uses KCP/KVCO instead of CPCON/LFCON */ - *cpcon = (data & PLLU_KCP_MASK) >> PLLU_KCP_SHIFT; - *lfcon = (data & PLLU_KVCO_MASK) >> PLLU_KVCO_SHIFT; -#endif + /* NOTE: On T210, cpcon/lfcon no longer exist, moved to KCP/KVCO */ + *cpcon = (data >> pllinfo->kcp_shift) & pllinfo->kcp_mask; + *lfcon = (data >> pllinfo->kvco_shift) & pllinfo->kvco_mask; + return 0; }
@@ -127,41 +125,25 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, u32 divp, u32 cpcon, u32 lfcon) { struct clk_pll *pll = NULL; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[clkid]; u32 misc_data, data;
if (clkid < (enum clock_id)TEGRA_CLK_PLLS) pll = get_pll(clkid);
/* - * We cheat by treating all PLL (except PLLU) in the same fashion. - * This works only because: - * - same fields are always mapped at same offsets, except DCCON - * - DCCON is always 0, doesn't conflict - * - M,N, P of PLLP values are ignored for PLLP - * NOTE: Above is no longer true with T210 - TBD: FIX THIS + * pllinfo has the m/n/p and kcp/kvco mask and shift + * values for all of the PLLs used in U-Boot, with any + * SoC differences accounted for. */ - misc_data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT); - -#if defined(CONFIG_TEGRA210) - /* T210 PLLU uses KCP/KVCO instead of cpcon/lfcon */ - if (clkid == CLOCK_ID_USB) { - /* preserve EN_LOCKDET, set by default */ - misc_data = readl(&pll->pll_misc); - misc_data |= (cpcon << PLLU_KCP_SHIFT) | - (lfcon << PLLU_KVCO_SHIFT); - } -#endif - data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) | - (0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT); - - if (clkid == CLOCK_ID_USB) -#if defined(CONFIG_TEGRA210) - data |= divp << PLLU_DIVP_SHIFT; -#else - data |= divp << PLLU_VCO_FREQ_SHIFT; -#endif - else - data |= divp << PLL_DIVP_SHIFT; + misc_data = readl(&pll->pll_misc); /* preserve EN_LOCKDET, etc. */ + misc_data &= ~(pllinfo->kcp_mask << pllinfo->kcp_shift) | (cpcon << pllinfo->kcp_shift); + misc_data &= ~(pllinfo->kvco_mask << pllinfo->kvco_shift) | (lfcon << pllinfo->kvco_shift); + + data = (divm << pllinfo->m_shift) | (divn << pllinfo->n_shift); + data |= divp << pllinfo->p_shift; + data |= (1 << PLL_ENABLE_SHIFT); /* BYPASS s/b 0 already */ + if (pll) { writel(misc_data, &pll->pll_misc); writel(data, &pll->pll_base); @@ -473,10 +455,9 @@ void reset_cmplx_set_enable(int cpu, int which, int reset) unsigned clock_get_rate(enum clock_id clkid) { struct clk_pll *pll; - u32 base; - u32 divm; - u64 parent_rate; - u64 rate; + u32 base, divm; + u64 parent_rate, rate; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[clkid];
parent_rate = osc_freq[clock_get_osc_freq()]; if (clkid == CLOCK_ID_OSC) @@ -487,13 +468,13 @@ unsigned clock_get_rate(enum clock_id clkid) return 0; base = readl(&pll->pll_base);
- /* Oh for bf_unpack()... */ - rate = parent_rate * ((base & PLL_DIVN_MASK) >> PLL_DIVN_SHIFT); - divm = (base & PLL_DIVM_MASK) >> PLL_DIVM_SHIFT; - if (clkid == CLOCK_ID_USB) - divm <<= (base & PLLU_VCO_FREQ_MASK) >> PLLU_VCO_FREQ_SHIFT; - else - divm <<= (base & PLL_DIVP_MASK) >> PLL_DIVP_SHIFT; + rate = parent_rate * ((base >> pllinfo->n_shift) & pllinfo->n_mask); + divm = (base >> pllinfo->m_shift) & pllinfo->m_mask; + /* + * PLLU uses p_mask/p_shift for VCO on all but T210, + * T210 uses normal DIVP. Handled in pllinfo table. + */ + divm <<= (base >> pllinfo->p_shift) & pllinfo->p_mask; do_div(rate, divm); return rate; } @@ -517,23 +498,23 @@ unsigned clock_get_rate(enum clock_id clkid) */ int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon) { - u32 base_reg; - u32 misc_reg; + u32 base_reg, misc_reg; struct clk_pll *pll; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[clkid];
pll = get_pll(clkid);
base_reg = readl(&pll->pll_base);
/* Set BYPASS, m, n and p to PLL_BASE */ - base_reg &= ~PLL_DIVM_MASK; - base_reg |= m << PLL_DIVM_SHIFT; + base_reg &= ~(pllinfo->m_mask << pllinfo->m_shift); + base_reg |= m << pllinfo->m_shift;
- base_reg &= ~PLL_DIVN_MASK; - base_reg |= n << PLL_DIVN_SHIFT; + base_reg &= ~(pllinfo->n_mask << pllinfo->n_shift); + base_reg |= n << pllinfo->n_shift;
- base_reg &= ~PLL_DIVP_MASK; - base_reg |= p << PLL_DIVP_SHIFT; + base_reg &= ~(pllinfo->p_mask << pllinfo->p_shift); + base_reg |= p << pllinfo->p_shift;
if (clkid == CLOCK_ID_PERIPH) { /* @@ -552,17 +533,10 @@ int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon) base_reg |= PLL_BYPASS_MASK; writel(base_reg, &pll->pll_base);
- /* Set cpcon to PLL_MISC */ + /* Set cpcon (KCP) to PLL_MISC */ misc_reg = readl(&pll->pll_misc); -#if !defined(CONFIG_TEGRA210) - misc_reg &= ~PLL_CPCON_MASK; - misc_reg |= cpcon << PLL_CPCON_SHIFT; -#else - /* T210 uses KCP instead, use the most common bit shift (PLLA/U/D2) */ - misc_reg &= ~PLLU_KCP_MASK; - misc_reg |= cpcon << PLLU_KCP_SHIFT; -#endif - + misc_reg &= ~(pllinfo->kcp_mask << pllinfo->kcp_shift); + misc_reg |= cpcon << pllinfo->kcp_shift; writel(misc_reg, &pll->pll_misc);
/* Enable PLL */ diff --git a/arch/arm/mach-tegra/cpu.c b/arch/arm/mach-tegra/cpu.c index b9391d6..ba04a56 100644 --- a/arch/arm/mach-tegra/cpu.c +++ b/arch/arm/mach-tegra/cpu.c @@ -181,6 +181,7 @@ static inline void pllx_set_iddq(void) int pllx_set_rate(struct clk_pll_simple *pll , u32 divn, u32 divm, u32 divp, u32 cpcon) { + struct clk_pll_info *pllinfo = &tegra_pll_info_table[CLOCK_ID_XCPU]; int chip = tegra_get_chip(); u32 reg; debug("%s entry\n", __func__); @@ -194,17 +195,21 @@ int pllx_set_rate(struct clk_pll_simple *pll , u32 divn, u32 divm, pllx_set_iddq();
/* Set BYPASS, m, n and p to PLLX_BASE */ - reg = PLL_BYPASS_MASK | (divm << PLL_DIVM_SHIFT); - reg |= ((divn << PLL_DIVN_SHIFT) | (divp << PLL_DIVP_SHIFT)); + reg = PLL_BYPASS_MASK | (divm << pllinfo->m_shift); + reg |= (divn << pllinfo->n_shift) | (divp << pllinfo->p_shift); writel(reg, &pll->pll_base);
/* Set cpcon to PLLX_MISC */ if (chip == CHIPID_TEGRA20 || chip == CHIPID_TEGRA30) - reg = (cpcon << PLL_CPCON_SHIFT); + reg = (cpcon << pllinfo->kcp_shift); else reg = 0;
- /* Set dccon to PLLX_MISC if freq > 600MHz */ + /* + * TODO(twarren@nvidia.com) Check which SoCs use DCCON + * and add to pllinfo table if needed! + */ + /* Set dccon to PLLX_MISC if freq > 600MHz */ if (divn > 600) reg |= (1 << PLL_DCCON_SHIFT); writel(reg, &pll->pll_misc); @@ -215,9 +220,10 @@ int pllx_set_rate(struct clk_pll_simple *pll , u32 divn, u32 divm, writel(reg, &pll->pll_base); debug("%s: base = 0x%08X\n", __func__, reg);
- /* Set lock_enable to PLLX_MISC */ + /* Set lock_enable to PLLX_MISC if lock_ena is valid (i.e. 0-31) */ reg = readl(&pll->pll_misc); - reg |= PLL_LOCK_ENABLE_MASK; + if (pllinfo->lock_ena < 32) + reg |= (1 << pllinfo->lock_ena); writel(reg, &pll->pll_misc); debug("%s: misc = 0x%08X\n", __func__, reg);
diff --git a/arch/arm/mach-tegra/tegra114/clock.c b/arch/arm/mach-tegra/tegra114/clock.c index d5194e1..f1831d3 100644 --- a/arch/arm/mach-tegra/tegra114/clock.c +++ b/arch/arm/mach-tegra/tegra114/clock.c @@ -1,17 +1,8 @@ /* - * Copyright (c) 2010-2014, NVIDIA CORPORATION. All rights reserved. + * (C) Copyright 2010-2015 + * NVIDIA Corporation <www.nvidia.com> * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. + * SPDX-License-Identifier: GPL-2.0+ */
/* Tegra114 Clock control functions */ @@ -435,6 +426,36 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { };
/* + * PLL divider shift/mask tables for all PLL IDs. + */ +struct clk_pll_info tegra_pll_info_table[CLOCK_ID_PLL_COUNT] = { + /* + * T114: some deviations from T2x/T30. + * NOTE: If kcp_mask/kvco_mask == 0, they're not used in that PLL (PLLX, etc.) + * If lock_ena or lock_det are >31, they're not used in that PLL. + */ + + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 24, .lock_det = 27, .kcp_shift = 28, .kcp_mask = 3, .kvco_shift = 27, .kvco_mask = 1 }, /* PLLC */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 0, .lock_det = 27, .kcp_shift = 1, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLM */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLP */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLA */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x01, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLU */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLD */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 0, .kcp_mask = 0, .kvco_shift = 0, .kvco_mask = 0 }, /* PLLX */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 9, .lock_det = 11, .kcp_shift = 6, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLE */ + { .m_shift = 0, .m_mask = 0x0F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLS (RESERVED) */ +}; + +/* * Get the oscillator frequency, from the corresponding hardware configuration * field. Note that T30/T114 support 3 new higher freqs, but we map back * to the old T20 freqs. Support for the higher oscillators is TBD. @@ -603,6 +624,8 @@ void clock_early_init(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll_info *pllinfo; + u32 data;
tegra30_set_up_pllp();
@@ -639,11 +662,15 @@ void clock_early_init(void) writel(0x00561600, &clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_out[1]);
/* PLLC_MISC: Set LOCK_ENABLE */ - writel(0x01000000, &clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_misc); + pllinfo = &tegra_pll_info_table[CLOCK_ID_CGENERAL]; + setbits_le32(&clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_misc, (1 << pllinfo->lock_ena)); udelay(2);
- /* PLLD_MISC: Set CLKENABLE, CPCON 12, LFCON 1 */ - writel(0x40000C10, &clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_misc); + /* PLLD_MISC: Set CLKENABLE, CPCON 12, LFCON 1, and enable lock */ + pllinfo = &tegra_pll_info_table[CLOCK_ID_DISPLAY]; + data = (12 << pllinfo->kcp_shift) | (1 << pllinfo->kvco_shift); + data |= (1 << PLLD_CLKENABLE) | (1 << pllinfo->lock_ena); + writel(data, &clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_misc); udelay(2); }
diff --git a/arch/arm/mach-tegra/tegra114/cpu.c b/arch/arm/mach-tegra/tegra114/cpu.c index 18dc1af..272a2ea 100644 --- a/arch/arm/mach-tegra/tegra114/cpu.c +++ b/arch/arm/mach-tegra/tegra114/cpu.c @@ -1,17 +1,8 @@ /* - * Copyright (c) 2010-2014, NVIDIA CORPORATION. All rights reserved. + * (C) Copyright 2010-2014 + * NVIDIA Corporation <www.nvidia.com> * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. + * SPDX-License-Identifier: GPL-2.0+ */
#include <common.h> @@ -31,7 +22,7 @@ static void enable_cpu_power_rail(void) struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; u32 reg;
- debug("enable_cpu_power_rail entry\n"); + debug("%s entry\n", __func__);
/* un-tristate PWR_I2C SCL/SDA, rest of the defaults are correct */ pinmux_tristate_disable(PMUX_PINGRP_PWR_I2C_SCL_PZ6); @@ -61,14 +52,15 @@ static void enable_cpu_power_rail(void) static void enable_cpu_clocks(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[CLOCK_ID_XCPU]; u32 reg;
- debug("enable_cpu_clocks entry\n"); + debug("%s entry\n", __func__);
/* Wait for PLL-X to lock */ do { reg = readl(&clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); - } while ((reg & PLL_LOCK_MASK) == 0); + } while ((reg & (1 << pllinfo->lock_det)) == 0);
/* Wait until all clocks are stable */ udelay(PLL_STABILIZATION_DELAY); @@ -87,7 +79,7 @@ static void remove_cpu_resets(void) struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; u32 reg;
- debug("remove_cpu_resets entry\n"); + debug("%s entry\n", __func__); /* Take the slow non-CPU partition out of reset */ reg = readl(&clkrst->crc_rst_cpulp_cmplx_clr); writel((reg | CLR_NONCPURESET), &clkrst->crc_rst_cpulp_cmplx_clr); @@ -111,7 +103,7 @@ static void remove_cpu_resets(void) }
/** - * The T114 requires some special clock initialization, including setting up + * Tegra114 requires some special clock initialization, including setting up * the DVC I2C, turning on MSELECT and selecting the G CPU cluster */ void t114_init_clocks(void) @@ -121,7 +113,7 @@ void t114_init_clocks(void) struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; u32 val;
- debug("t114_init_clocks entry\n"); + debug("%s entry\n", __func__);
/* Set active CPU cluster to G */ clrbits_le32(&flow->cluster_control, 1); @@ -189,7 +181,7 @@ void t114_init_clocks(void) reset_set_enable(PERIPH_ID_MC1, 0); reset_set_enable(PERIPH_ID_DVFS, 0);
- debug("t114_init_clocks exit\n"); + debug("%s exit\n", __func__); }
static bool is_partition_powered(u32 partid) @@ -238,10 +230,9 @@ static void power_partition(u32 partid)
void powerup_cpus(void) { - debug("powerup_cpus entry\n"); - /* We boot to the fast cluster */ - debug("powerup_cpus entry: G cluster\n"); + debug("%s entry: G cluster\n", __func__); + /* Power up the fast cluster rail partition */ power_partition(CRAIL);
@@ -256,7 +247,7 @@ void start_cpu(u32 reset_vector) { u32 imme, inst;
- debug("start_cpu entry, reset_vector = %x\n", reset_vector); + debug("%s entry, reset_vector = %x\n", __func__, reset_vector);
t114_init_clocks();
@@ -302,7 +293,7 @@ void start_cpu(u32 reset_vector) inst |= 0xea000000; writel(inst, 0x4003fffc);
- /* Write to orignal location for compatibility */ + /* Write to original location for compatibility */ writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR);
/* If the CPU(s) don't already have power, power 'em up */ diff --git a/arch/arm/mach-tegra/tegra124/clock.c b/arch/arm/mach-tegra/tegra124/clock.c index b955848..9126218 100644 --- a/arch/arm/mach-tegra/tegra124/clock.c +++ b/arch/arm/mach-tegra/tegra124/clock.c @@ -1,5 +1,5 @@ /* - * (C) Copyright 2013 + * (C) Copyright 2013-2015 * NVIDIA Corporation <www.nvidia.com> * * SPDX-License-Identifier: GPL-2.0+ @@ -566,6 +566,36 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { };
/* + * PLL divider shift/mask tables for all PLL IDs. + */ +struct clk_pll_info tegra_pll_info_table[CLOCK_ID_PLL_COUNT] = { + /* + * T124: same as T114, some deviations from T2x/T30. + * NOTE: If kcp_mask/kvco_mask == 0, they're not used in that PLL (PLLX, etc.) + * If lock_ena or lock_det are >31, they're not used in that PLL. + */ + + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 24, .lock_det = 27, .kcp_shift = 28, .kcp_mask = 3, .kvco_shift = 27, .kvco_mask = 1 }, /* PLLC */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 0, .lock_det = 27, .kcp_shift = 1, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLM */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLP */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLA */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x01, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLU */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLD */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 0, .kcp_mask = 0, .kvco_shift = 0, .kvco_mask = 0 }, /* PLLX */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 9, .lock_det = 11, .kcp_shift = 6, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLE */ + { .m_shift = 0, .m_mask = 0x0F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLS (RESERVED) */ +}; + +/* * Get the oscillator frequency, from the corresponding hardware configuration * field. Note that Tegra30+ support 3 new higher freqs, but we map back * to the old T20 freqs. Support for the higher oscillators is TBD. @@ -772,6 +802,8 @@ void clock_early_init(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll_info *pllinfo; + u32 data;
tegra30_set_up_pllp();
@@ -808,11 +840,15 @@ void clock_early_init(void) writel(0x00561600, &clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_out[1]);
/* PLLC_MISC: Set LOCK_ENABLE */ - writel(0x01000000, &clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_misc); + pllinfo = &tegra_pll_info_table[CLOCK_ID_CGENERAL]; + setbits_le32(&clkrst->crc_pll[CLOCK_ID_CGENERAL].pll_misc, (1 << pllinfo->lock_ena)); udelay(2);
- /* PLLD_MISC: Set CLKENABLE, CPCON 12, LFCON 1 */ - writel(0x40000C10, &clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_misc); + /* PLLD_MISC: Set CLKENABLE, CPCON 12, LFCON 1, and enable lock */ + pllinfo = &tegra_pll_info_table[CLOCK_ID_DISPLAY]; + data = (12 << pllinfo->kcp_shift) | (1 << pllinfo->kvco_shift); + data |= (1 << PLLD_CLKENABLE) | (1 << pllinfo->lock_ena); + writel(data, &clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_misc); udelay(2); }
diff --git a/arch/arm/mach-tegra/tegra124/cpu.c b/arch/arm/mach-tegra/tegra124/cpu.c index 974f203..0aca658 100644 --- a/arch/arm/mach-tegra/tegra124/cpu.c +++ b/arch/arm/mach-tegra/tegra124/cpu.c @@ -23,7 +23,7 @@ static void enable_cpu_power_rail(void) { struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
- debug("enable_cpu_power_rail entry\n"); + debug("%s entry\n", __func__);
/* un-tristate PWR_I2C SCL/SDA, rest of the defaults are correct */ pinmux_tristate_disable(PMUX_PINGRP_PWR_I2C_SCL_PZ6); @@ -45,15 +45,16 @@ static void enable_cpu_power_rail(void) static void enable_cpu_clocks(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[CLOCK_ID_XCPU]; u32 reg;
- debug("enable_cpu_clocks entry\n"); + debug("%s entry\n", __func__);
/* Wait for PLL-X to lock */ do { reg = readl(&clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); debug("%s: PLLX base = 0x%08X\n", __func__, reg); - } while ((reg & PLL_LOCK_MASK) == 0); + } while ((reg & (1 << pllinfo->lock_det)) == 0);
debug("%s: PLLX locked, delay for stable clocks\n", __func__); /* Wait until all clocks are stable */ @@ -83,7 +84,7 @@ static void remove_cpu_resets(void) struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; u32 reg;
- debug("remove_cpu_resets entry\n"); + debug("%s entry\n", __func__);
/* Take the slow and fast partitions out of reset */ reg = CLR_NONCPURESET; @@ -105,7 +106,7 @@ static void remove_cpu_resets(void) }
/** - * The Tegra124 requires some special clock initialization, including setting up + * Tegra124 requires some special clock initialization, including setting up * the DVC I2C, turning on MSELECT and selecting the G CPU cluster */ void tegra124_init_clocks(void) @@ -116,7 +117,7 @@ void tegra124_init_clocks(void) (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; u32 val;
- debug("tegra124_init_clocks entry\n"); + debug("%s entry\n", __func__);
/* Set active CPU cluster to G */ clrbits_le32(&flow->cluster_control, 1); @@ -188,7 +189,7 @@ void tegra124_init_clocks(void) reset_set_enable(PERIPH_ID_MSELECT, 0); reset_set_enable(PERIPH_ID_DVFS, 0);
- debug("tegra124_init_clocks exit\n"); + debug("%s exit\n", __func__); }
static bool is_partition_powered(u32 partid) @@ -223,31 +224,29 @@ static void power_partition(u32 partid)
void powerup_cpus(void) { - debug("powerup_cpus entry\n"); - /* We boot to the fast cluster */ - debug("powerup_cpus entry: G cluster\n"); + debug("%s entry: G cluster\n", __func__);
/* Power up the fast cluster rail partition */ - debug("powerup_cpus: CRAIL\n"); + debug("%s: CRAIL\n", __func__); power_partition(CRAIL);
/* Power up the fast cluster non-CPU partition */ - debug("powerup_cpus: C0NC\n"); + debug("%s: C0NC\n", __func__); power_partition(C0NC);
/* Power up the fast cluster CPU0 partition */ - debug("powerup_cpus: CE0\n"); + debug("%s: CE0\n", __func__); power_partition(CE0);
- debug("powerup_cpus: done\n"); + debug("%s: done\n", __func__); }
void start_cpu(u32 reset_vector) { struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
- debug("start_cpu entry, reset_vector = %x\n", reset_vector); + debug("%s entry, reset_vector = %x\n", __func__, reset_vector);
tegra124_init_clocks();
@@ -261,5 +260,5 @@ void start_cpu(u32 reset_vector) remove_cpu_resets(); writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR); powerup_cpus(); - debug("start_cpu exit, should continue @ reset_vector\n"); + debug("%s exit, should continue @ reset_vector\n", __func__); } diff --git a/arch/arm/mach-tegra/tegra20/clock.c b/arch/arm/mach-tegra/tegra20/clock.c index 7b9e10c..df9f412 100644 --- a/arch/arm/mach-tegra/tegra20/clock.c +++ b/arch/arm/mach-tegra/tegra20/clock.c @@ -1,5 +1,7 @@ /* * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010-2015 + * NVIDIA Corporation <www.nvidia.com> * * SPDX-License-Identifier: GPL-2.0+ */ @@ -355,6 +357,36 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { };
/* + * PLL divider shift/mask tables for all PLL IDs. + */ +struct clk_pll_info tegra_pll_info_table[CLOCK_ID_PLL_COUNT] = { + /* + * T20 and T25 + * NOTE: If kcp_mask/kvco_mask == 0, they're not used in that PLL (PLLX, etc.) + * If lock_ena or lock_det are >31, they're not used in that PLL. + */ + + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 24, .lock_det = 27, .kcp_shift = 28, .kcp_mask = 3, .kvco_shift = 27, .kvco_mask = 1 }, /* PLLC */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 0, .p_mask = 0, + .lock_ena = 0, .lock_det = 27, .kcp_shift = 1, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLM */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLP */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLA */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x01, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLU */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLD */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 0, .kcp_mask = 0, .kvco_shift = 0, .kvco_mask = 0 }, /* PLLX */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 9, .lock_det = 11, .kcp_shift = 6, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLE */ + { .m_shift = 0, .m_mask = 0x0F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 0, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLS */ +}; + +/* * Get the oscillator frequency, from the corresponding hardware configuration * field. T20 has 4 frequencies that it supports. */ diff --git a/arch/arm/mach-tegra/tegra210/clock.c b/arch/arm/mach-tegra/tegra210/clock.c index 7ac2a06..a7b3a94 100644 --- a/arch/arm/mach-tegra/tegra210/clock.c +++ b/arch/arm/mach-tegra/tegra210/clock.c @@ -631,6 +631,34 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { };
/* + * PLL divider shift/mask tables for all PLL IDs. + */ +struct clk_pll_info tegra_pll_info_table[CLOCK_ID_PLL_COUNT] = { + /* + * NOTE: If kcp_mask/kvco_mask == 0, they're not used in that PLL (PLLC, etc.) + * If lock_ena or lock_det are >31, they're not used in that PLL (PLLC, etc.) + */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 10, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x1F, + .lock_ena = 32, .lock_det = 27, .kcp_shift = 0, .kcp_mask = 0, .kvco_shift = 0, .kvco_mask = 0 }, /* PLLC */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x1F, + .lock_ena = 4, .lock_det = 27, .kcp_shift = 1, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLM */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 10, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x1F, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 0, .kcp_mask = 3, .kvco_shift = 2, .kvco_mask = 1 }, /* PLLP */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x1F, + .lock_ena = 28, .lock_det = 27, .kcp_shift = 25, .kcp_mask = 3, .kvco_shift = 24, .kvco_mask = 1 }, /* PLLA */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 16, .p_mask = 0x1F, + .lock_ena = 29, .lock_det = 27, .kcp_shift = 25, .kcp_mask = 3, .kvco_shift = 24, .kvco_mask = 1 }, /* PLLU */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 11, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 23, .kcp_mask = 3, .kvco_shift = 22, .kvco_mask = 1 }, /* PLLD */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x1F, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 1, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLX */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 9, .lock_det = 11, .kcp_shift = 6, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLE */ + { .m_shift = 0, .m_mask = 0, .n_shift = 0, .n_mask = 0, .p_shift = 0, .p_mask = 0, + .lock_ena = 0, .lock_det = 0, .kcp_shift = 0, .kcp_mask = 0, .kvco_shift = 0, .kvco_mask = 0 }, /* PLLS (gone)*/ +}; + +/* * Get the oscillator frequency, from the corresponding hardware configuration * field. Note that Tegra30+ support 3 new higher freqs, but we map back * to the old T20 freqs. Support for the higher oscillators is TBD. @@ -903,6 +931,7 @@ void clock_early_init(void) { struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[CLOCK_ID_DISPLAY]; u32 data;
tegra210_setup_pllp(); @@ -957,7 +986,7 @@ void clock_early_init(void) udelay(2);
/* PLLD_MISC: Set CLKENABLE and LOCK_DETECT bits */ - data = (1 << PLLD_ENABLE_CLK) | (1 << PLLD_EN_LCKDET); + data = (1 << PLLD_ENABLE_CLK) | (1 << pllinfo->lock_ena); writel(data, &clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_misc); udelay(2); } diff --git a/arch/arm/mach-tegra/tegra30/clock.c b/arch/arm/mach-tegra/tegra30/clock.c index 0eb0f0a..4267bb2 100644 --- a/arch/arm/mach-tegra/tegra30/clock.c +++ b/arch/arm/mach-tegra/tegra30/clock.c @@ -1,17 +1,8 @@ /* - * Copyright (c) 2010-2014, NVIDIA CORPORATION. All rights reserved. + * (C) Copyright 2010-2015 + * NVIDIA Corporation <www.nvidia.com> * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. + * SPDX-License-Identifier: GPL-2.0+ */
/* Tegra30 Clock control functions */ @@ -415,6 +406,36 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { };
/* + * PLL divider shift/mask tables for all PLL IDs. + */ +struct clk_pll_info tegra_pll_info_table[CLOCK_ID_PLL_COUNT] = { + /* + * T30: some deviations from T2x. + * NOTE: If kcp_mask/kvco_mask == 0, they're not used in that PLL (PLLX, etc.) + * If lock_ena or lock_det are >31, they're not used in that PLL. + */ + + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 24, .lock_det = 27, .kcp_shift = 28, .kcp_mask = 3, .kvco_shift = 27, .kvco_mask = 1 }, /* PLLC */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 0, .p_mask = 0, + .lock_ena = 0, .lock_det = 27, .kcp_shift = 1, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLM */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLP */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLA */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x01, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLU */ + { .m_shift = 0, .m_mask = 0x1F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 22, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLD */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 20, .p_mask = 0x0F, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 0, .kcp_mask = 0, .kvco_shift = 0, .kvco_mask = 0 }, /* PLLX */ + { .m_shift = 0, .m_mask = 0xFF, .n_shift = 8, .n_mask = 0xFF, .p_shift = 0, .p_mask = 0, + .lock_ena = 9, .lock_det = 11, .kcp_shift = 6, .kcp_mask = 3, .kvco_shift = 0, .kvco_mask = 1 }, /* PLLE */ + { .m_shift = 0, .m_mask = 0x0F, .n_shift = 8, .n_mask = 0x3FF, .p_shift = 20, .p_mask = 0x07, + .lock_ena = 18, .lock_det = 27, .kcp_shift = 8, .kcp_mask = 0xF, .kvco_shift = 4, .kvco_mask = 0xF }, /* PLLS (RESERVED) */ +}; + +/* * Get the oscillator frequency, from the corresponding hardware configuration * field. Note that T30 supports 3 new higher freqs, but we map back * to the old T20 freqs. Support for the higher oscillators is TBD.

On Wed, 2015-07-29 at 13:13 -0700, Tom Warren wrote:
Added PLL variables (dividers mask/shift, lock enable/detect, etc.) to new pllinfo struct for each Soc/PLL. PLLA/C/D/E/M/P/U/X.
Used pllinfo struct in all clock functions, validated on T210. Should be equivalent to prior code on T124/114/30/20 but needs test.
Corrections to divm mask vs shift and T20/30 divN masks thanks to Marcel Ziswiler.
Signed-off-by: Tom Warren twarren@nvidia.com
While this boots on Colibri T20/T30 as well as Apalis T30 there is still something askew on T20 as e.g. USB Ethernet is more than 6 times slower than before. T30 on the other hand seems to work fine. I will try to find some time to investigate further.

Marcel,
-----Original Message----- From: Marcel Ziswiler [mailto:marcel@ziswiler.com] Sent: Tuesday, August 04, 2015 1:33 AM To: Tom Warren; u-boot@lists.denx.de Cc: tomcwarren3959@gmail.com; Stephen Warren; Thierry Reding; sjg@chromium.org Subject: Re: [PATCH 2/2] Tegra: PLL: use per-SoC pllinfo table instead of PLL_DIVM/N/P, etc.
On Wed, 2015-07-29 at 13:13 -0700, Tom Warren wrote:
Added PLL variables (dividers mask/shift, lock enable/detect, etc.) to new pllinfo struct for each Soc/PLL. PLLA/C/D/E/M/P/U/X.
Used pllinfo struct in all clock functions, validated on T210. Should be equivalent to prior code on T124/114/30/20 but needs test.
Corrections to divm mask vs shift and T20/30 divN masks thanks to Marcel Ziswiler.
Signed-off-by: Tom Warren twarren@nvidia.com
While this boots on Colibri T20/T30 as well as Apalis T30 there is still something askew on T20 as e.g. USB Ethernet is more than 6 times slower than before. T30 on the other hand seems to work fine. I will try to find some time to investigate further.
Thanks. My T20/T30 boards are moth-balled, so I don't test on them. T210 USB is fine. If you can provide CAR register dumps (0x60006000 - 0x60006FFF) on T20 I can take a look.
Appreciate any testing you can do. This won't go in to u-boot-tegra until you are satisified.
Tom -- nvpublic

On Tue, 2015-08-04 at 15:36 +0000, Tom Warren wrote:
Thanks. My T20/T30 boards are moth-balled, so I don't test on them. T210 USB is fine. If you can provide CAR register dumps (0x60006000 - 0x60006FFF) on T20 I can take a look.
I had a look at it. Looks like you missed the cpcon stuff for PLLX both on T20 as well as T30 but while the former's PLLX won't even lock any more the later seems to still lock OK. Nonetheless I will send a patch to fix this for both shortly.
Appreciate any testing you can do. This won't go in to u-boot-tegra until you are satisified.
Your patience with us remaining T20/T30 users is highly appreciated as we still hope to be selling those Colibris for another 10 resp. 13 years!

Hi,
On 29 July 2015 at 14:13, Tom Warren twarren@nvidia.com wrote:
Added PLL variables (dividers mask/shift, lock enable/detect, etc.) to new pllinfo struct for each Soc/PLL. PLLA/C/D/E/M/P/U/X.
Used pllinfo struct in all clock functions, validated on T210. Should be equivalent to prior code on T124/114/30/20 but needs test.
Corrections to divm mask vs shift and T20/30 divN masks thanks to Marcel Ziswiler.
Signed-off-by: Tom Warren twarren@nvidia.com
arch/arm/include/asm/arch-tegra/clk_rst.h | 32 +------ arch/arm/include/asm/arch-tegra/clock.h | 21 +++++ arch/arm/include/asm/arch-tegra210/clock-tables.h | 1 + arch/arm/mach-tegra/clock.c | 108 ++++++++-------------- arch/arm/mach-tegra/cpu.c | 18 ++-- arch/arm/mach-tegra/tegra114/clock.c | 57 +++++++++--- arch/arm/mach-tegra/tegra114/cpu.c | 39 +++----- arch/arm/mach-tegra/tegra124/clock.c | 44 ++++++++- arch/arm/mach-tegra/tegra124/cpu.c | 31 +++---- arch/arm/mach-tegra/tegra20/clock.c | 32 +++++++ arch/arm/mach-tegra/tegra210/clock.c | 31 ++++++- arch/arm/mach-tegra/tegra30/clock.c | 45 ++++++--- 12 files changed, 285 insertions(+), 174 deletions(-)
This breaks the display on Nyan.
I think I found two problems - I'll send out a patch.
Regards, Simon

On 07/29/2015 02:13 PM, Tom Warren wrote:
Added 38.4MHz/48MHz entries to pll_x_table for CPU PLL. Needs to be measured - should be close to 700MHz (1.4G/2).
Note that some freqs aren't in the PLLU table in T210 TRM (13, 26MHz), so I used the 12MHz table entry for them. They shouldn't be selected since they're not viable T210 OSC freqs.
Since there are now 2 new OSC defines, all tables (pll_x_table, PLLU) had to increase by two entries, but since 38.4/48MHz are not viable osc freqs on T20/30/114, etc, they're just set to 0.
I didn't look at all of the numbers in detail, but these patches look like a step in the right direction, so the series, Acked-by: Stephen Warren swarren@nvidia.com Tested-by: Stephen Warren swarren@nvidia.com (tested upstream this time)
participants (5)
-
Marcel Ziswiler
-
Simon Glass
-
Stephen Warren
-
Tom Warren
-
Tom Warren