int clock_get_pll_input_khz(void) { u32 osc_ctrl = read32(CLK_RST_REG(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; }
static void enable_core_clocks(int cpu) { const uint32_t cpu0_clocks = CRC_RST_CPUG_CLR_CPU0 | CRC_RST_CPUG_CLR_DBG0 | CRC_RST_CPUG_CLR_CORE0 | CRC_RST_CPUG_CLR_CX0; const uint32_t cpu1_clocks = CRC_RST_CPUG_CLR_CPU1 | CRC_RST_CPUG_CLR_DBG1 | CRC_RST_CPUG_CLR_CORE1 | CRC_RST_CPUG_CLR_CX1; /* Clear reset of CPU components. */ if (cpu == 0) write32(CLK_RST_REG(rst_cpug_cmplx_clr), cpu0_clocks); else write32(CLK_RST_REG(rst_cpug_cmplx_clr), cpu1_clocks); }
/* 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_RST_REG(clk_src_uarta), CLK_SRC_DEV_ID(UARTA, CLK_M) << CLK_SOURCE_SHIFT | CLK_UART_DIV_OVERRIDE | CLK_DIVIDER(TEGRA_CLK_M_KHZ, 1900)); clock_enable_clear_reset_l(CLK_L_UARTA); }
/* 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)); u32 misc2 = ((kvco << PLLM_MISC2_KVCO_SHIFT) | (kcp << PLLM_MISC2_KCP_SHIFT)); u32 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). */ write32(CLK_RST_REG(pllm_misc1), misc1); write32(CLK_RST_REG(pllm_misc2), misc2); /* PLLM.BASE needs BYPASS=0, different from general init_pll */ base = read32(CLK_RST_REG(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)); write32(CLK_RST_REG(pllm_base), base); setbits_le32(CLK_RST_REG(pllm_base), PLL_BASE_ENABLE); /* stable_time is required, before we can start to check lock. */ udelay(stable_time); while (!(read32(CLK_RST_REG(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_REG(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); write32(CLK_RST_REG(clk_src_emc), emc_source); udelay(IO_STABILIZATION_DELAY); }
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_REG(clk_src_sor), SOR0_CLK_SEL0 | SOR0_CLK_SEL1); }
/* 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_REG(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); write32(cfg, scfg); init_pll(CLK_RST_REG(plldp_base), CLK_RST_REG(plldp_misc), osc_table[osc].plldp, PLLDPD2_MISC_LOCK_ENABLE); /* leave dither and undoc bits set, release clamp */ scfg = (1<<28) | (1<<24); write32(cfg, scfg); }
static int ccplex_start(void) { struct stopwatch sw; const long timeout_ms = 1500; const uint32_t handshake_mask = 1; const uint32_t cxreset1_mask = 1 << 21; uint32_t reg; struct tegra_pmc_regs * const pmc = PMC_REGS; /* Set the handshake bit to be knocked down. */ write32(&pmc->scratch118, handshake_mask); /* Assert nCXRSET[1] */ reg = read32(CLK_RST_REG(rst_cpu_cmplx_set)); reg |= cxreset1_mask; write32(CLK_RST_REG(rst_cpu_cmplx_set), reg); stopwatch_init_msecs_expire(&sw, timeout_ms); while (1) { reg = read32(&pmc->scratch118); /* Wait for the bit to be knocked down. */ if ((reg & handshake_mask) != handshake_mask) break; if (stopwatch_expired(&sw)) { printk(BIOS_DEBUG, "MTS handshake timeout.\n"); return -1; } } printk(BIOS_DEBUG, "MTS handshake took %ld usecs.\n", stopwatch_duration_usecs(&sw)); return 0; }
static void enable_core_clocks(int cpu) { const uint32_t cpu_clocks[CONFIG_MAX_CPUS] = { [0] = CRC_RST_CPUG_CLR_CPU0 | CRC_RST_CPUG_CLR_DBG0 | CRC_RST_CPUG_CLR_CORE0 | CRC_RST_CPUG_CLR_CX0, [1] = CRC_RST_CPUG_CLR_CPU1 | CRC_RST_CPUG_CLR_DBG1 | CRC_RST_CPUG_CLR_CORE1 | CRC_RST_CPUG_CLR_CX1, [2] = CRC_RST_CPUG_CLR_CPU2 | CRC_RST_CPUG_CLR_DBG2 | CRC_RST_CPUG_CLR_CORE2 | CRC_RST_CPUG_CLR_CX2, [3] = CRC_RST_CPUG_CLR_CPU3 | CRC_RST_CPUG_CLR_DBG3 | CRC_RST_CPUG_CLR_CORE3 | CRC_RST_CPUG_CLR_CX3, }; assert (cpu < CONFIG_MAX_CPUS); /* Clear reset of CPU components. */ write32(CLK_RST_REG(rst_cpug_cmplx_clr), cpu_clocks[cpu]); }
static void enable_cpu_power_partitions(void) { /* Bring up fast cluster, non-CPU, CPU0, CPU1, CPU2 and CPU3 parts. */ power_ungate_partition(POWER_PARTID_CRAIL); power_ungate_partition(POWER_PARTID_C0NC); power_ungate_partition(POWER_PARTID_CE0); if (IS_ENABLED(CONFIG_ARM64_USE_ARM_TRUSTED_FIRMWARE)) { /* * Deassert reset signal of all the secondary CPUs. * PMC and flow controller will take over the power sequence * controller in the ATF. */ uint32_t reg = CRC_RST_CPUG_CLR_CPU1 | CRC_RST_CPUG_CLR_DBG1 | CRC_RST_CPUG_CLR_CORE1 | CRC_RST_CPUG_CLR_CX1 | CRC_RST_CPUG_CLR_CPU2 | CRC_RST_CPUG_CLR_DBG2 | CRC_RST_CPUG_CLR_CORE2 | CRC_RST_CPUG_CLR_CX2 | CRC_RST_CPUG_CLR_CPU3 | CRC_RST_CPUG_CLR_DBG3 | CRC_RST_CPUG_CLR_CORE3 | CRC_RST_CPUG_CLR_CX3; write32(CLK_RST_REG(rst_cpug_cmplx_clr), reg); } }
static void set_clock_sources(void) { /* UARTA gets PLLP, deactivate CLK_UART_DIV_OVERRIDE */ write32(CLK_RST_REG(clk_src_uarta), PLLP << CLK_SOURCE_SHIFT); }
void clock_reset_x(u32 bit) { clock_reset_dev(CLK_RST_REG(rst_dev_x_set), CLK_RST_REG(rst_dev_x_clr), bit); }
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? */ write32(CLK_RST_REG(pllc_misc2), 0x2b << 17 | 0xb << 9); /* Max out the AVP clock before everything else (need PLLC for that). */ init_pll(CLK_RST_REG(pllc_base), CLK_RST_REG(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(CLK_RST_REG(clk_sys_rate), /* pclk = hclk = sclk/2 */ 1 << HCLK_DIVISOR_SHIFT | 0 << PCLK_DIVISOR_SHIFT); write32(CLK_RST_REG(pllc_out), CLK_DIVIDER(TEGRA_PLLC_KHZ, 300000) << PLL_OUT_RATIO_SHIFT | PLL_OUT_CLKEN | PLL_OUT_RSTN); write32(CLK_RST_REG(sclk_brst_pol), /* sclk = 300 MHz */ SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT | SCLK_SOURCE_PLLC_OUT1 << SCLK_RUN_SHIFT); /* Change the oscillator drive strength (from U-Boot -- why?) */ clrsetbits_le32(CLK_RST_REG(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); /* Set up PLLP_OUT(1|2|3|4) divisor to generate (9.6|48|102|204)MHz */ write32(CLK_RST_REG(pllp_outa), (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); write32(CLK_RST_REG(pllp_outb), (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); /* init pllu */ init_pll(CLK_RST_REG(pllu_base), CLK_RST_REG(pllu_misc), osc_table[osc].pllu, PLLUD_MISC_LOCK_ENABLE); init_utmip_pll(); graphics_pll(); }
/* * Init PLLD clock source. * * @frequency: the requested plld frequency * * Return the plld frequency if success, otherwise return 0. */ u32 clock_configure_plld(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 that undershoot or overshoot the 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, rounded_rate = frequency; 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 0; } 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_WARNING, "%s: Failed to match output frequency %u, " "best difference is %u.\n", __func__, frequency, best_diff); rounded_rate = (ref / plld.m * plld.n) >> plld.p; } printk(BIOS_DEBUG, "%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n", __func__, rounded_rate, ref, plld.m, plld.n, plld.p, plld.cpcon); init_pll(CLK_RST_REG(plld_base), CLK_RST_REG(plld_misc), plld, (PLLUD_MISC_LOCK_ENABLE | PLLD_MISC_CLK_ENABLE)); if (rounded_rate != frequency) printk(BIOS_DEBUG, "PLLD rate: %u vs %u\n", rounded_rate, frequency); return rounded_rate; }
void sor_clock_start(void) { /* uses PLLP, has a non-standard bit layout. */ setbits_le32(CLK_RST_REG(clk_src_sor), SOR0_CLK_SEL0); }
/* 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 (read32(CLK_RST_REG(osc_ctrl)) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; }
osc_table[osc].pllu, PLLUD_MISC_LOCK_ENABLE); init_utmip_pll(); graphics_pll(); } void clock_grp_enable_clear_reset(u32 val, u32* clk_enb_set_reg, u32 *rst_dev_clr_reg) { write32(clk_enb_set_reg, val); udelay(IO_STABILIZATION_DELAY); write32(rst_dev_clr_reg, val); } static u32 * const clk_enb_set_arr[DEV_CONFIG_BLOCKS] = { CLK_RST_REG(clk_enb_l_set), CLK_RST_REG(clk_enb_h_set), CLK_RST_REG(clk_enb_u_set), CLK_RST_REG(clk_enb_v_set), CLK_RST_REG(clk_enb_w_set), CLK_RST_REG(clk_enb_x_set), }; static u32 * const clk_enb_clr_arr[DEV_CONFIG_BLOCKS] = { CLK_RST_REG(clk_enb_l_clr), CLK_RST_REG(clk_enb_h_clr), CLK_RST_REG(clk_enb_u_clr), CLK_RST_REG(clk_enb_v_clr), CLK_RST_REG(clk_enb_w_clr), CLK_RST_REG(clk_enb_x_clr), };