X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=arch%2Farm%2Fmach-tegra%2Fclock.c;h=f75c6c664a25a0071c68197f3fcf527b889c3582;hb=36e5f7ce1c64b4fa0a12c82f84086f5be560b706;hp=11c7435505c1d17d53ccf10c4040f640d5a4afde;hpb=09f455dca74973ef5e42311162c8dff7e83d44a2;p=oweals%2Fu-boot.git diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 11c7435505..f75c6c664a 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -17,10 +17,13 @@ /* Tegra SoC common clock control functions */ #include +#include #include #include #include +#include #include +#include #include #include #include @@ -41,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 */ @@ -80,13 +85,23 @@ static struct clk_pll *get_pll(enum clock_id clkid) (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; assert(clock_id_is_pll(clkid)); + if (clkid >= (enum clock_id)TEGRA_CLK_PLLS) { + debug("%s: Invalid PLL %d\n", __func__, clkid); + return NULL; + } return &clkrst->crc_pll[clkid]; } +__weak struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid) +{ + return NULL; +} + 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); @@ -95,12 +110,13 @@ 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; + /* 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; } @@ -108,27 +124,48 @@ int clock_ll_read_pll(enum clock_id clkid, u32 *divm, u32 *divn, unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, u32 divp, u32 cpcon, u32 lfcon) { - struct clk_pll *pll = get_pll(clkid); - u32 data; + struct clk_pll *pll = NULL; + struct clk_pll_info *pllinfo = &tegra_pll_info_table[clkid]; + struct clk_pll_simple *simple_pll = NULL; + u32 misc_data, data; + + if (clkid < (enum clock_id)TEGRA_CLK_PLLS) { + pll = get_pll(clkid); + } else { + simple_pll = clock_get_simple_pll(clkid); + if (!simple_pll) { + debug("%s: Uknown simple PLL %d\n", __func__, clkid); + return 0; + } + } /* - * 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 + * 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. + * + * Preserve EN_LOCKDET, etc. */ - data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT); - writel(data, &pll->pll_misc); - - data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) | - (0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT); - - if (clkid == CLOCK_ID_USB) - data |= divp << PLLU_VCO_FREQ_SHIFT; + if (pll) + misc_data = readl(&pll->pll_misc); else - data |= divp << PLL_DIVP_SHIFT; - writel(data, &pll->pll_base); + misc_data = readl(&simple_pll->pll_misc); + misc_data &= ~(pllinfo->kcp_mask << pllinfo->kcp_shift); + misc_data |= cpcon << pllinfo->kcp_shift; + misc_data &= ~(pllinfo->kvco_mask << pllinfo->kvco_shift); + misc_data |= 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); + } else { + writel(misc_data, &simple_pll->pll_misc); + writel(data, &simple_pll->pll_base); + } /* calculate the stable time */ return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US; @@ -151,12 +188,37 @@ void clock_ll_set_source_divisor(enum periph_id periph_id, unsigned source, writel(value, reg); } -void clock_ll_set_source(enum periph_id periph_id, unsigned source) +int clock_ll_set_source_bits(enum periph_id periph_id, int mux_bits, + unsigned source) { u32 *reg = get_periph_source_reg(periph_id); - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK, - source << OUT_CLK_SOURCE_31_30_SHIFT); + switch (mux_bits) { + case MASK_BITS_31_30: + clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK, + source << OUT_CLK_SOURCE_31_30_SHIFT); + break; + + case MASK_BITS_31_29: + clrsetbits_le32(reg, OUT_CLK_SOURCE_31_29_MASK, + source << OUT_CLK_SOURCE_31_29_SHIFT); + break; + + case MASK_BITS_31_28: + clrsetbits_le32(reg, OUT_CLK_SOURCE_31_28_MASK, + source << OUT_CLK_SOURCE_31_28_SHIFT); + break; + + default: + return -1; + } + + return 0; +} + +void clock_ll_set_source(enum periph_id periph_id, unsigned source) +{ + clock_ll_set_source_bits(periph_id, MASK_BITS_31_30, source); } /** @@ -305,25 +367,7 @@ static int adjust_periph_pll(enum periph_id periph_id, int source, if (source < 0) return -1; - switch (mux_bits) { - case MASK_BITS_31_30: - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK, - source << OUT_CLK_SOURCE_31_30_SHIFT); - break; - - case MASK_BITS_31_29: - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_29_MASK, - source << OUT_CLK_SOURCE_31_29_SHIFT); - break; - - case MASK_BITS_31_28: - clrsetbits_le32(reg, OUT_CLK_SOURCE_31_28_MASK, - source << OUT_CLK_SOURCE_31_28_SHIFT); - break; - - default: - return -1; - } + clock_ll_set_source_bits(periph_id, mux_bits, source); udelay(2); return 0; @@ -417,28 +461,46 @@ void reset_cmplx_set_enable(int cpu, int which, int reset) writel(mask, &clkrst->crc_cpu_cmplx_clr); } +unsigned int __weak clk_m_get_rate(unsigned int parent_rate) +{ + return parent_rate; +} + 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) return parent_rate; + if (clkid == CLOCK_ID_CLK_M) + return clk_m_get_rate(parent_rate); + pll = get_pll(clkid); + if (!pll) + 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. + */ +#ifdef CONFIG_TEGRA210 + /* + * PLLP's primary output (pllP_out0) on T210 is the VCO, and divp is + * not applied. pllP_out2 does have divp applied. All other pllP_outN + * are divided down from pllP_out0. We only support pllP_out0 in + * U-Boot at the time of writing this comment. + */ + if (clkid != CLOCK_ID_PERIPH) +#endif + divm <<= (base >> pllinfo->p_shift) & pllinfo->p_mask; do_div(rate, divm); return rate; } @@ -462,23 +524,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) { /* @@ -497,10 +559,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); - misc_reg &= ~PLL_CPCON_MASK; - misc_reg |= cpcon << PLL_CPCON_SHIFT; + misc_reg &= ~(pllinfo->kcp_mask << pllinfo->kcp_shift); + misc_reg |= cpcon << pllinfo->kcp_shift; writel(misc_reg, &pll->pll_misc); /* Enable PLL */ @@ -528,7 +590,7 @@ void clock_ll_start_uart(enum periph_id periph_id) reset_set_enable(periph_id, 0); } -#ifdef CONFIG_OF_CONTROL +#if CONFIG_IS_ENABLED(OF_CONTROL) int clock_decode_periph_id(const void *blob, int node) { enum periph_id id; @@ -543,7 +605,7 @@ int clock_decode_periph_id(const void *blob, int node) assert(clock_periph_id_isvalid(id)); return id; } -#endif /* CONFIG_OF_CONTROL */ +#endif /* CONFIG_IS_ENABLED(OF_CONTROL) */ int clock_verify(void) { @@ -560,20 +622,24 @@ 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_OSC] = clock_get_rate(CLOCK_ID_OSC); - pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; + 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_XCPU] = clock_get_rate(CLOCK_ID_XCPU); + pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; + pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC); + pll_rate[CLOCK_ID_CLK_M] = clock_get_rate(CLOCK_ID_CLK_M); + debug("Osc = %d\n", pll_rate[CLOCK_ID_OSC]); + debug("CLKM = %d\n", pll_rate[CLOCK_ID_CLK_M]); + 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]); - - /* Do any special system timer/TSC setup */ - arch_timer_init(); } static void set_avp_clock_source(u32 src) @@ -594,6 +660,7 @@ static void set_avp_clock_source(u32 src) /* * This function is useful on Tegra30, and any later SoCs that have compatible * PLLP configuration registers. + * NOTE: Not used on Tegra210 - see tegra210_setup_pllp in T210 clock.c */ void tegra30_set_up_pllp(void) { @@ -667,3 +734,18 @@ void tegra30_set_up_pllp(void) set_avp_clock_source(SCLK_SOURCE_PLLP_OUT4); } + +int clock_external_output(int clk_id) +{ + struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + + if (clk_id >= 1 && clk_id <= 3) { + setbits_le32(&pmc->pmc_clk_out_cntrl, + 1 << (2 + (clk_id - 1) * 8)); + } else { + printf("%s: Unknown output clock id %d\n", __func__, clk_id); + return -EINVAL; + } + + return 0; +}