diff options
author | Tom Warren <twarren@nvidia.com> | 2015-06-25 09:50:44 -0700 |
---|---|---|
committer | Tom Warren <twarren@nvidia.com> | 2015-08-05 15:22:51 -0700 |
commit | 722e000ccd7226c5cd071590b5361620eb0b126c (patch) | |
tree | 257ddcaf4039dd6722e743e8a1f4035c2f85387f /arch/arm/mach-tegra/clock.c | |
parent | 3e8650c0f9cc7fb29bd75c11d0173768fcc80203 (diff) | |
download | u-boot-722e000ccd7226c5cd071590b5361620eb0b126c.tar.gz |
Tegra: PLL: use per-SoC pllinfo table instead of PLL_DIVM/N/P, etc.
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. Thanks
to Marcel Ziswiler for corrections to the T20/T30 values.
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Tested-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Signed-off-by: Tom Warren <twarren@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/clock.c')
-rw-r--r-- | arch/arm/mach-tegra/clock.c | 108 |
1 files changed, 41 insertions, 67 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index d0eebd21c1..3b2b4ffd2a 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 */ |