summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/clock.c
diff options
context:
space:
mode:
authorTom Warren <twarren@nvidia.com>2015-06-25 09:50:44 -0700
committerTom Warren <twarren@nvidia.com>2015-08-05 15:22:51 -0700
commit722e000ccd7226c5cd071590b5361620eb0b126c (patch)
tree257ddcaf4039dd6722e743e8a1f4035c2f85387f /arch/arm/mach-tegra/clock.c
parent3e8650c0f9cc7fb29bd75c11d0173768fcc80203 (diff)
downloadu-boot-imx-722e000ccd7226c5cd071590b5361620eb0b126c.zip
u-boot-imx-722e000ccd7226c5cd071590b5361620eb0b126c.tar.gz
u-boot-imx-722e000ccd7226c5cd071590b5361620eb0b126c.tar.bz2
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.c108
1 files changed, 41 insertions, 67 deletions
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 */