diff options
Diffstat (limited to 'src/soc/nvidia/tegra132/clock.c')
-rw-r--r-- | src/soc/nvidia/tegra132/clock.c | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/src/soc/nvidia/tegra132/clock.c b/src/soc/nvidia/tegra132/clock.c new file mode 100644 index 0000000000..5237712ef3 --- /dev/null +++ b/src/soc/nvidia/tegra132/clock.c @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * Copyright 2014 Google Inc. + * + * 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/>. + */ +#include <console/console.h> +#include <delay.h> +#include <arch/io.h> +#include <soc/addressmap.h> +#include <soc/clock.h> +#include <stdlib.h> +#include "clk_rst.h" +#include "flow.h" +#include "maincpu.h" +#include "pmc.h" +#include "sysctr.h" + +static struct clk_rst_ctlr *clk_rst = (void *)TEGRA_CLK_RST_BASE; +static struct flow_ctlr *flow = (void *)TEGRA_FLOW_BASE; +static struct tegra_pmc_regs *pmc = (void *)TEGRA_PMC_BASE; +static struct sysctr_regs *sysctr = (void *)TEGRA_SYSCTR0_BASE; + +struct pll_dividers { + u32 n : 10; + u32 m : 8; + u32 p : 4; + u32 cpcon : 4; + u32 lfcon : 4; + u32 : 2; +}; + +/* Some PLLs have more restrictive divider bit lengths or are missing some + * fields. Make sure to use the right struct in the osc_table definition to get + * compile-time checking, but keep the bits aligned with struct pll_dividers so + * they can be used interchangeably at run time. Add new formats as required. */ +struct pllcx_dividers { + u32 n : 8; + u32 : 2; + u32 m : 8; + u32 p : 4; + u32 : 10; +}; +struct pllpad_dividers { + u32 n : 10; + u32 m : 5; + u32 : 3; + u32 p : 3; + u32 : 1; + u32 cpcon : 4; + u32 : 6; +}; +struct pllu_dividers { + u32 n : 10; + u32 m : 5; + u32 : 3; + u32 p : 1; + u32 : 3; + u32 cpcon : 4; + u32 lfcon : 4; + u32 : 2; +}; + +union __attribute__((transparent_union)) pll_fields { + u32 raw; + struct pll_dividers div; + struct pllcx_dividers cx; + struct pllpad_dividers pad; + struct pllu_dividers u; +}; + +/* This table defines the frequency dividers for every PLL to turn the external + * OSC clock into the frequencies defined by TEGRA_PLL*_KHZ in soc/clock.h. + * All PLLs have three dividers (n, m and p), with the governing formula for + * the output frequency being CF = (IN / m), VCO = CF * n and OUT = VCO / (2^p). + * All divisor configurations must meet the PLL's constraints for VCO and CF: + * PLLX: 12 MHz < CF < 50 MHz, 700 MHz < VCO < 3000 MHz + * PLLC: 12 MHz < CF < 50 MHz, 600 MHz < VCO < 1400 MHz + * PLLM: 12 MHz < CF < 50 MHz, 400 MHz < VCO < 1066 MHz + * PLLP: 1 MHz < CF < 6 MHz, 200 MHz < VCO < 700 MHz + * PLLD: 1 MHz < CF < 6 MHz, 500 MHz < VCO < 1000 MHz + * PLLU: 1 MHz < CF < 6 MHz, 480 MHz < VCO < 960 MHz + * PLLDP: 12 MHz < CF < 38 MHz, 600 MHz < VCO < 1200 MHz + * (values taken from Linux' drivers/clk/tegra/clk-tegra124.c). */ +struct { + int khz; + struct pllcx_dividers pllx; /* target: CONFIG_PLLX_KHZ */ + struct pllcx_dividers pllc; /* target: 600 MHz */ + /* PLLM is set up dynamically by clock_sdram(). */ + /* PLLP is hardwired to 408 MHz in HW (unless we set BASE_OVRD). */ + struct pllu_dividers pllu; /* target; 960 MHz */ + struct pllcx_dividers plldp; /* target; 270 MHz */ + /* PLLDP treats p differently (OUT = VCO / (p + 1) for p < 6). */ +} static const osc_table[16] = { + [OSC_FREQ_12]{ + .khz = 12000, + .pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0}, + .pllc = {.n = 50, .m = 1, .p = 0}, + .pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2}, + .plldp = {.n = 90, .m = 1, .p = 3}, + }, + [OSC_FREQ_13]{ + .khz = 13000, + .pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m = 1, .p = 0}, + .pllc = {.n = 46, .m = 1, .p = 0}, /* 598.0 MHz */ + .pllu = {.n = 960, .m = 13, .p = 0, .cpcon = 12, .lfcon = 2}, + .plldp = {.n = 83, .m = 1, .p = 3}, /* 269.8 MHz */ + }, + [OSC_FREQ_16P8]{ + .khz = 16800, + .pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m = 1, .p = 0}, + .pllc = {.n = 71, .m = 1, .p = 1}, /* 596.4 MHz */ + .pllu = {.n = 400, .m = 7, .p = 0, .cpcon = 5, .lfcon = 2}, + .plldp = {.n = 64, .m = 1, .p = 3}, /* 268.8 MHz */ + }, + [OSC_FREQ_19P2]{ + .khz = 19200, + .pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0}, + .pllc = {.n = 62, .m = 1, .p = 1}, /* 595.2 MHz */ + .pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2}, + .plldp = {.n = 56, .m = 1, .p = 3}, /* 268.8 MHz */ + }, + [OSC_FREQ_26]{ + .khz = 26000, + .pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m = 1, .p = 0}, + .pllc = {.n = 23, .m = 1, .p = 0}, /* 598.0 MHz */ + .pllu = {.n = 960, .m = 26, .p = 0, .cpcon = 12, .lfcon = 2}, + .plldp = {.n = 83, .m = 2, .p = 3}, /* 269.8 MHz */ + }, + /* These oscillators get predivided as PLL inputs... n/m/p divisors for + * 38.4 should always match 19.2, and 48 should always match 12. */ + [OSC_FREQ_38P4]{ + .khz = 38400, + .pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0}, + .pllc = {.n = 62, .m = 1, .p = 1}, /* 595.2 MHz */ + .pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2}, + .plldp = {.n = 56, .m = 1, .p = 3}, /* 268.8 MHz */ + }, + [OSC_FREQ_48]{ + .khz = 48000, + .pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0}, + .pllc = {.n = 50, .m = 1, .p = 0}, + .pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2}, + .plldp = {.n = 90, .m = 1, .p = 3}, + }, +}; + +/* Get the oscillator frequency, from the corresponding hardware + * configuration field. This is actually a per-soc thing. Avoid the + * temptation to make it common. + */ +static u32 clock_get_osc_bits(void) +{ + return (readl(&clk_rst->osc_ctrl) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; +} + +int clock_get_osc_khz(void) +{ + return osc_table[clock_get_osc_bits()].khz; +} + +int clock_get_pll_input_khz(void) +{ + u32 osc_ctrl = readl(&clk_rst->osc_ctrl); + u32 osc_bits = (osc_ctrl & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; + u32 pll_ref_div = (osc_ctrl & OSC_PREDIV_MASK) >> OSC_PREDIV_SHIFT; + return osc_table[osc_bits].khz >> pll_ref_div; +} + +void clock_init_arm_generic_timer(void) +{ + uint32_t freq = clock_get_osc_khz() * 1000; + // Set the cntfrq register. + __asm__ __volatile__("mcr p15, 0, %0, c14, c0, 0\n" :: "r"(freq)); + + // Record the system timer frequency. + write32(freq, &sysctr->cntfid0); + // Enable the system counter. + uint32_t cntcr = read32(&sysctr->cntcr); + cntcr |= SYSCTR_CNTCR_EN | SYSCTR_CNTCR_HDBG; + write32(cntcr, &sysctr->cntcr); +} + +#define SOR0_CLK_SEL0 (1 << 14) +#define SOR0_CLK_SEL1 (1 << 15) + +void sor_clock_stop(void) +{ + /* The Serial Output Resource clock has to be off + * before we start the plldp. Learned the hard way. + * FIXME: this has to be cleaned up a bit more. + * Waiting on some new info from Nvidia. + */ + clrbits_le32(&clk_rst->clk_src_sor, SOR0_CLK_SEL0 | SOR0_CLK_SEL1); +} + +void sor_clock_start(void) +{ + /* uses PLLP, has a non-standard bit layout. */ + setbits_le32(&clk_rst->clk_src_sor, SOR0_CLK_SEL0); +} + +static void init_pll(u32 *base, u32 *misc, const union pll_fields pll, u32 lock) +{ + u32 dividers = pll.div.n << PLL_BASE_DIVN_SHIFT | + pll.div.m << PLL_BASE_DIVM_SHIFT | + pll.div.p << PLL_BASE_DIVP_SHIFT; + u32 misc_con = pll.div.cpcon << PLL_MISC_CPCON_SHIFT | + pll.div.lfcon << PLL_MISC_LFCON_SHIFT; + + /* Write dividers but BYPASS the PLL while we're messing with it. */ + writel(dividers | PLL_BASE_BYPASS, base); + /* + * Set Lock bit, CPCON and LFCON fields (default to 0 if it doesn't + * exist for this PLL) + */ + writel(lock | misc_con, misc); + + /* Enable PLL and take it back out of BYPASS */ + writel(dividers | PLL_BASE_ENABLE, base); + + /* Wait for lock ready */ + while (!(readl(base) & PLL_BASE_LOCK)); +} + +static void init_utmip_pll(void) +{ + int khz = clock_get_pll_input_khz(); + + /* Shut off PLL crystal clock while we mess with it */ + clrbits_le32(&clk_rst->utmip_pll_cfg2, 1 << 30); /* PHY_XTAL_CLKEN */ + udelay(1); + + write32(80 << 16 | /* (rst) phy_divn */ + 1 << 8 | /* (rst) phy_divm */ + 0, &clk_rst->utmip_pll_cfg0); /* 960MHz * 1 / 80 == 12 MHz */ + + write32(div_round_up(khz, 8000) << 27 | /* pllu_enbl_cnt / 8 (1us) */ + 0 << 16 | /* PLLU pwrdn */ + 0 << 14 | /* pll_enable pwrdn */ + 0 << 12 | /* pll_active pwrdn */ + div_round_up(khz, 102) << 0 | /* phy_stbl_cnt / 256 (2.5ms) */ + 0, &clk_rst->utmip_pll_cfg1); + + /* TODO: TRM can't decide if actv is 5us or 10us, keep an eye on it */ + write32(0 << 24 | /* SAMP_D/XDEV pwrdn */ + div_round_up(khz, 3200) << 18 | /* phy_actv_cnt / 16 (5us) */ + div_round_up(khz, 256) << 6 | /* pllu_stbl_cnt / 256 (1ms) */ + 0 << 4 | /* SAMP_C/USB3 pwrdn */ + 0 << 2 | /* SAMP_B/XHOST pwrdn */ + 0 << 0 | /* SAMP_A/USBD pwrdn */ + 0, &clk_rst->utmip_pll_cfg2); + + setbits_le32(&clk_rst->utmip_pll_cfg2, 1 << 30); /* PHY_XTAL_CLKEN */ +} + +/* Graphics just has to be different. There's a few more bits we + * need to set in here, but it makes sense just to restrict all the + * special bits to this one function. + */ +static void graphics_pll(void) +{ + int osc = clock_get_osc_bits(); + u32 *cfg = &clk_rst->plldp_ss_cfg; + /* the vendor code sets the dither bit (28) + * an undocumented bit (24) + * and clamp while we mess with it (22) + * Dither is pretty important to display port + * so we really do need to handle these bits. + * I'm not willing to not clamp it, even if + * it might "mostly work" with it not set, + * I don't want to find out in a few months + * that it is needed. + */ + u32 scfg = (1<<28) | (1<<24) | (1<<22); + writel(scfg, cfg); + init_pll(&clk_rst->plldp_base, &clk_rst->plldp_misc, + osc_table[osc].plldp, PLLDPD2_MISC_LOCK_ENABLE); + /* leave dither and undoc bits set, release clamp */ + scfg = (1<<28) | (1<<24); + writel(scfg, cfg); + + /* disp1 will be set when panel information (pixel clock) is + * retrieved (clock_display). + */ +} + +/* Init PLLD clock source. */ +int +clock_display(u32 frequency) +{ + /** + * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz + * = (cf * n) >> p, where 1MHz < cf < 6MHz + * = ((ref / m) * n) >> p + * + * Iterate the possible values of p (3 bits, 2^7) to find out a minimum + * safe vco, then find best (m, n). since m has only 5 bits, we can + * iterate all possible values. Note Tegra 124 supports 11 bits for n, + * but our pll_fields has only 10 bits for n. + * + * Note values undershoot or overshoot target output frequency may not + * work if the values are not in "safe" range by panel specification. + */ + struct pllpad_dividers plld = { 0 }; + u32 ref = clock_get_pll_input_khz() * 1000, m, n, p = 0; + u32 cf, vco; + u32 diff, best_diff; + const u32 max_m = 1 << 5, max_n = 1 << 10, max_p = 1 << 3, + mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz, + min_cf = 1 * mhz, max_cf = 6 * mhz; + + for (vco = frequency; vco < min_vco && p < max_p; p++) + vco <<= 1; + + if (vco < min_vco || vco > max_vco) { + printk(BIOS_ERR, "%s: Cannot find out a supported VCO" + " for Frequency (%u).\n", __func__, frequency); + return -1; + } + + plld.p = p; + best_diff = vco; + + for (m = 1; m < max_m && best_diff; m++) { + cf = ref / m; + if (cf < min_cf) + break; + if (cf > max_cf) + continue; + + n = vco / cf; + if (n >= max_n) + continue; + + diff = vco - n * cf; + if (n + 1 < max_n && diff > cf / 2) { + n++; + diff = cf - diff; + } + + if (diff >= best_diff) + continue; + + best_diff = diff; + plld.m = m; + plld.n = n; + } + + if (plld.n < 50) + plld.cpcon = 2; + else if (plld.n < 300) + plld.cpcon = 3; + else if (plld.n < 600) + plld.cpcon = 8; + else + plld.cpcon = 12; + + if (best_diff) { + printk(BIOS_ERR, "%s: Failed to match output frequency %u, " + "best difference is %u.\n", __func__, frequency, + best_diff); + } + + printk(BIOS_DEBUG, "%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n", + __func__, (ref / plld.m * plld.n) >> plld.p, ref, plld.m, plld.n, + plld.p, plld.cpcon); + + init_pll(&clk_rst->plld_base, &clk_rst->plld_misc, plld, + (PLLUD_MISC_LOCK_ENABLE | PLLD_MISC_CLK_ENABLE)); + return 0; +} + +/* Initialize the UART and put it on CLK_M so we can use it during clock_init(). + * Will later move it to PLLP in clock_config(). The divisor must be very small + * to accomodate 12KHz OSCs, so we override the 16.0 UART divider with the 15.1 + * CLK_SOURCE divider to get more precision. (This might still not be enough for + * some OSCs... if you use 13KHz, be prepared to have a bad time.) The 1900 has + * been determined through trial and error (must lead to div 13 at 24MHz). */ +void clock_early_uart(void) +{ + write32(CLK_M << CLK_SOURCE_SHIFT | CLK_UART_DIV_OVERRIDE | + CLK_DIVIDER(TEGRA_CLK_M_KHZ, 1900), &clk_rst->clk_src_uarta); + setbits_le32(&clk_rst->clk_out_enb_l, CLK_L_UARTA); + udelay(2); + clrbits_le32(&clk_rst->rst_dev_l, CLK_L_UARTA); +} + +/* Enable output clock (CLK1~3) for external peripherals. */ +void clock_external_output(int clk_id) +{ + switch (clk_id) { + case 1: + setbits_le32(&pmc->clk_out_cntrl, 1 << 2); + break; + case 2: + setbits_le32(&pmc->clk_out_cntrl, 1 << 10); + break; + case 3: + setbits_le32(&pmc->clk_out_cntrl, 1 << 18); + break; + default: + printk(BIOS_CRIT, "ERROR: Unknown output clock id %d\n", + clk_id); + break; + } +} + +/* Start PLLM for SDRAM. */ +void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 ph45, u32 ph90, + u32 ph135, u32 kvco, u32 kcp, u32 stable_time, u32 emc_source, + u32 same_freq) +{ + u32 misc1 = ((setup << PLLM_MISC1_SETUP_SHIFT) | + (ph45 << PLLM_MISC1_PD_LSHIFT_PH45_SHIFT) | + (ph90 << PLLM_MISC1_PD_LSHIFT_PH90_SHIFT) | + (ph135 << PLLM_MISC1_PD_LSHIFT_PH135_SHIFT)), + misc2 = ((kvco << PLLM_MISC2_KVCO_SHIFT) | + (kcp << PLLM_MISC2_KCP_SHIFT)), + base; + + if (same_freq) + emc_source |= CLK_SOURCE_EMC_MC_EMC_SAME_FREQ; + else + emc_source &= ~CLK_SOURCE_EMC_MC_EMC_SAME_FREQ; + + /* + * Note PLLM_BASE.PLLM_OUT1_RSTN must be in RESET_ENABLE mode, and + * PLLM_BASE.ENABLE must be in DISABLE state (both are the default + * values after coldboot reset). + */ + + writel(misc1, &clk_rst->pllm_misc1); + writel(misc2, &clk_rst->pllm_misc2); + + /* PLLM.BASE needs BYPASS=0, different from general init_pll */ + base = readl(&clk_rst->pllm_base); + base &= ~(PLLCMX_BASE_DIVN_MASK | PLLCMX_BASE_DIVM_MASK | + PLLM_BASE_DIVP_MASK | PLL_BASE_BYPASS); + base |= ((m << PLL_BASE_DIVM_SHIFT) | (n << PLL_BASE_DIVN_SHIFT) | + (p << PLL_BASE_DIVP_SHIFT)); + writel(base, &clk_rst->pllm_base); + + setbits_le32(&clk_rst->pllm_base, PLL_BASE_ENABLE); + /* stable_time is required, before we can start to check lock. */ + udelay(stable_time); + + while (!(readl(&clk_rst->pllm_base) & PLL_BASE_LOCK)) { + udelay(1); + } + /* + * After PLLM reports being locked, we have to delay 10us before + * enabling PLLM_OUT. + */ + udelay(10); + + /* Put OUT1 out of reset state (start to output). */ + setbits_le32(&clk_rst->pllm_out, PLLM_OUT1_RSTN_RESET_DISABLE); + + /* Enable and start MEM(MC) and EMC. */ + clock_enable_clear_reset(0, CLK_H_MEM | CLK_H_EMC, 0, 0, 0, 0); + writel(emc_source, &clk_rst->clk_src_emc); + udelay(IO_STABILIZATION_DELAY); +} + +void clock_cpu0_config_and_reset(void *entry) +{ + void * const evp_cpu_reset = (uint8_t *)TEGRA_EVP_BASE + 0x100; + + write32(CONFIG_STACK_TOP, &maincpu_stack_pointer); + write32((uintptr_t)entry, &maincpu_entry_point); + write32((uintptr_t)&maincpu_setup, evp_cpu_reset); + + /* Set active CPU cluster to G */ + clrbits_le32(&flow->cluster_control, 1); + + // Set up cclk_brst and divider. + write32((CRC_CCLK_BRST_POL_PLLX_OUT0 << 0) | + (CRC_CCLK_BRST_POL_PLLX_OUT0 << 4) | + (CRC_CCLK_BRST_POL_PLLX_OUT0 << 8) | + (CRC_CCLK_BRST_POL_PLLX_OUT0 << 12) | + (CRC_CCLK_BRST_POL_CPU_STATE_RUN << 28), + &clk_rst->cclk_brst_pol); + write32(CRC_SUPER_CCLK_DIVIDER_SUPER_CDIV_ENB, + &clk_rst->super_cclk_div); + + // Enable the clocks for CPUs 0-3. + uint32_t cpu_cmplx_clr = read32(&clk_rst->clk_cpu_cmplx_clr); + cpu_cmplx_clr |= CRC_CLK_CLR_CPU0_STP | CRC_CLK_CLR_CPU1_STP | + CRC_CLK_CLR_CPU2_STP | CRC_CLK_CLR_CPU3_STP; + write32(cpu_cmplx_clr, &clk_rst->clk_cpu_cmplx_clr); + + // Enable other CPU related clocks. + setbits_le32(&clk_rst->clk_out_enb_l, CLK_L_CPU); + setbits_le32(&clk_rst->clk_out_enb_v, CLK_V_CPUG); + setbits_le32(&clk_rst->clk_out_enb_v, CLK_V_CPULP); + + // Disable the reset on the non-CPU parts of the fast cluster. + write32(CRC_RST_CPUG_CLR_NONCPU, + &clk_rst->rst_cpug_cmplx_clr); + // Disable the various resets on the CPUs. + write32(CRC_RST_CPUG_CLR_CPU0 | CRC_RST_CPUG_CLR_CPU1 | + CRC_RST_CPUG_CLR_CPU2 | CRC_RST_CPUG_CLR_CPU3 | + CRC_RST_CPUG_CLR_DBG0 | CRC_RST_CPUG_CLR_DBG1 | + CRC_RST_CPUG_CLR_DBG2 | CRC_RST_CPUG_CLR_DBG3 | + CRC_RST_CPUG_CLR_CORE0 | CRC_RST_CPUG_CLR_CORE1 | + CRC_RST_CPUG_CLR_CORE2 | CRC_RST_CPUG_CLR_CORE3 | + CRC_RST_CPUG_CLR_CX0 | CRC_RST_CPUG_CLR_CX1 | + CRC_RST_CPUG_CLR_CX2 | CRC_RST_CPUG_CLR_CX3 | + CRC_RST_CPUG_CLR_L2 | CRC_RST_CPUG_CLR_PDBG, + &clk_rst->rst_cpug_cmplx_clr); + + // Disable the reset on the non-CPU parts of the slow cluster. + write32(CRC_RST_CPULP_CLR_NONCPU, + &clk_rst->rst_cpulp_cmplx_clr); + // Disable the various resets on the LP CPU. + write32(CRC_RST_CPULP_CLR_CPU0 | CRC_RST_CPULP_CLR_DBG0 | + CRC_RST_CPULP_CLR_CORE0 | CRC_RST_CPULP_CLR_CX0 | + CRC_RST_CPULP_CLR_L2 | CRC_RST_CPULP_CLR_PDBG, + &clk_rst->rst_cpulp_cmplx_clr); +} + +void clock_halt_avp(void) +{ + for (;;) { + write32(FLOW_EVENT_JTAG | FLOW_EVENT_LIC_IRQ | + FLOW_EVENT_GIC_IRQ | FLOW_MODE_WAITEVENT, + &flow->halt_cop_events); + } +} + +void clock_init(void) +{ + u32 osc = clock_get_osc_bits(); + + /* Set PLLC dynramp_step A to 0x2b and B to 0xb (from U-Boot -- why? */ + writel(0x2b << 17 | 0xb << 9, &clk_rst->pllc_misc2); + + /* Max out the AVP clock before everything else (need PLLC for that). */ + init_pll(&clk_rst->pllc_base, &clk_rst->pllc_misc, + osc_table[osc].pllc, PLLC_MISC_LOCK_ENABLE); + + /* Typical ratios are 1:2:2 or 1:2:3 sclk:hclk:pclk (See: APB DMA + * features section in the TRM). */ + write32(1 << HCLK_DIVISOR_SHIFT | 0 << PCLK_DIVISOR_SHIFT, + &clk_rst->clk_sys_rate); /* pclk = hclk = sclk/2 */ + write32(CLK_DIVIDER(TEGRA_PLLC_KHZ, 300000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_CLKEN | PLL_OUT_RSTN, &clk_rst->pllc_out); + write32(SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT | + SCLK_SOURCE_PLLC_OUT1 << SCLK_RUN_SHIFT, + &clk_rst->sclk_brst_pol); /* sclk = 300 MHz */ + + /* Change the oscillator drive strength (from U-Boot -- why?) */ + clrsetbits_le32(&clk_rst->osc_ctrl, OSC_XOFS_MASK, + OSC_DRIVE_STRENGTH << OSC_XOFS_SHIFT); + + /* + * Ambiguous quote from u-boot. TODO: what's this mean? + * "should update same value in PMC_OSC_EDPD_OVER XOFS + * field for warmboot " + */ + clrsetbits_le32(&pmc->osc_edpd_over, PMC_OSC_EDPD_OVER_XOFS_MASK, + OSC_DRIVE_STRENGTH << PMC_OSC_EDPD_OVER_XOFS_SHIFT); + + /* Disable IDDQ for PLLX before we set it up (from U-Boot -- why?) */ + clrbits_le32(&clk_rst->pllx_misc3, PLLX_IDDQ_MASK); + + /* Set up PLLP_OUT(1|2|3|4) divisor to generate (9.6|48|102|204)MHz */ + write32((CLK_DIVIDER(TEGRA_PLLP_KHZ, 9600) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT1_SHIFT | + (CLK_DIVIDER(TEGRA_PLLP_KHZ, 48000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT2_SHIFT, + &clk_rst->pllp_outa); + write32((CLK_DIVIDER(TEGRA_PLLP_KHZ, 102000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT3_SHIFT | + (CLK_DIVIDER(TEGRA_PLLP_KHZ, 204000) << PLL_OUT_RATIO_SHIFT | + PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT4_SHIFT, + &clk_rst->pllp_outb); + + /* init pllx */ + init_pll(&clk_rst->pllx_base, &clk_rst->pllx_misc, + osc_table[osc].pllx, PLLPAXS_MISC_LOCK_ENABLE); + + /* init pllu */ + init_pll(&clk_rst->pllu_base, &clk_rst->pllu_misc, + osc_table[osc].pllu, PLLUD_MISC_LOCK_ENABLE); + + init_utmip_pll(); + graphics_pll(); +} + +void clock_enable_clear_reset(u32 l, u32 h, u32 u, u32 v, u32 w, u32 x) +{ + if (l) writel(l, &clk_rst->clk_enb_l_set); + if (h) writel(h, &clk_rst->clk_enb_h_set); + if (u) writel(u, &clk_rst->clk_enb_u_set); + if (v) writel(v, &clk_rst->clk_enb_v_set); + if (w) writel(w, &clk_rst->clk_enb_w_set); + if (x) writel(x, &clk_rst->clk_enb_x_set); + + /* Give clocks time to stabilize. */ + udelay(IO_STABILIZATION_DELAY); + + if (l) writel(l, &clk_rst->rst_dev_l_clr); + if (h) writel(h, &clk_rst->rst_dev_h_clr); + if (u) writel(u, &clk_rst->rst_dev_u_clr); + if (v) writel(v, &clk_rst->rst_dev_v_clr); + if (w) writel(w, &clk_rst->rst_dev_w_clr); + if (x) writel(x, &clk_rst->rst_dev_x_clr); +} + +void clock_reset_l(u32 bit) +{ + writel(bit, &clk_rst->rst_dev_l_set); + udelay(1); + writel(bit, &clk_rst->rst_dev_l_clr); +} + +void clock_reset_h(u32 bit) +{ + writel(bit, &clk_rst->rst_dev_h_set); + udelay(1); + writel(bit, &clk_rst->rst_dev_h_clr); +} + +void clock_reset_u(u32 bit) +{ + writel(bit, &clk_rst->rst_dev_u_set); + udelay(1); + writel(bit, &clk_rst->rst_dev_u_clr); +} + +void clock_reset_v(u32 bit) +{ + writel(bit, &clk_rst->rst_dev_v_set); + udelay(1); + writel(bit, &clk_rst->rst_dev_v_clr); +} + +void clock_reset_w(u32 bit) +{ + writel(bit, &clk_rst->rst_dev_w_set); + udelay(1); + writel(bit, &clk_rst->rst_dev_w_clr); +} + +void clock_reset_x(u32 bit) +{ + writel(bit, &clk_rst->rst_dev_x_set); + udelay(1); + writel(bit, &clk_rst->rst_dev_x_clr); +} |