diff options
Diffstat (limited to 'plat/gxb/pll.c')
-rw-r--r-- | plat/gxb/pll.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/plat/gxb/pll.c b/plat/gxb/pll.c new file mode 100644 index 0000000..5dd5bb8 --- /dev/null +++ b/plat/gxb/pll.c @@ -0,0 +1,271 @@ + +/* + * arch/arm/cpu/armv8/common/firmware/plat/gxb/pll.c + * + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <pll.h> +#include <string.h> +#include <asm/arch/watchdog.h> +#include <stdio.h> +#include <asm/arch/secure_apb.h> +#include <timer.h> +#include <stdio.h> +#include <asm/arch/timing.h> + +unsigned lock_check_loop = 0; +extern pll_set_t __pll_setting; +static pll_set_t * p_pll_set = &__pll_setting; + +unsigned int pll_init(void){ + // Switch clk81 to the oscillator input + // romcode might have already programmed clk81 to a PLL + Wr( HHI_MPEG_CLK_CNTL, Rd(HHI_MPEG_CLK_CNTL) & ~(1 << 8) ); + // Switch sys clk to oscillator, the SYS CPU might have already been programmed + clocks_set_sys_cpu_clk( 0, 0, 0, 0); + + //SYS PLL,FIX PLL bangap + Wr(HHI_MPLL_CNTL6, Rd(HHI_MPLL_CNTL6)|(1<<26)); + _udelay(100); + + unsigned int sys_pll_cntl = 0; + if ((p_pll_set->cpu_clk >= 600) && (p_pll_set->cpu_clk <= 1200)) { + sys_pll_cntl = (1<<16/*OD*/) | (1<<9/*N*/) | (p_pll_set->cpu_clk / 12/*M*/); + } + else if ((p_pll_set->cpu_clk > 1200) && (p_pll_set->cpu_clk <= 2000)) { + sys_pll_cntl = (0<<16/*OD*/) | (1<<9/*N*/) | (p_pll_set->cpu_clk / 24/*M*/); + } + //Init SYS pll + do { + Wr(HHI_SYS_PLL_CNTL, Rd(HHI_SYS_PLL_CNTL)|(1<<29)); + Wr(HHI_SYS_PLL_CNTL2, CFG_SYS_PLL_CNTL_2); + Wr(HHI_SYS_PLL_CNTL3, CFG_SYS_PLL_CNTL_3); + Wr(HHI_SYS_PLL_CNTL4, CFG_SYS_PLL_CNTL_4); + Wr(HHI_SYS_PLL_CNTL5, CFG_SYS_PLL_CNTL_5); + Wr(HHI_SYS_PLL_CNTL, ((1<<30)|(1<<29)|sys_pll_cntl)); // A9 clock + Wr(HHI_SYS_PLL_CNTL, Rd(HHI_SYS_PLL_CNTL)&(~(1<<29))); + _udelay(20); + } while(pll_lock_check(HHI_SYS_PLL_CNTL, "SYS PLL")); + clocks_set_sys_cpu_clk( 1, 0, 0, 0); // Connect SYS CPU to the PLL divider output + + sys_pll_cntl = Rd(HHI_SYS_PLL_CNTL); + unsigned cpu_clk = (24/ \ + ((sys_pll_cntl>>9)&0x1F)* \ + (sys_pll_cntl&0x1FF)/ \ + (1<<((sys_pll_cntl>>16)&0x3))); + /* cpu clk = 24/N*M/2^OD */ + serial_puts("CPU clk: "); + serial_put_dec(cpu_clk); + serial_puts("MHz\n"); + + //FIXED PLL + Wr(HHI_MPLL_CNTL4, CFG_MPLL_CNTL_4); + Wr(HHI_MPLL_CNTL, Rd(HHI_MPLL_CNTL)|(1<<29)); + _udelay(200); + Wr(HHI_MPLL_CNTL2, CFG_MPLL_CNTL_2); + Wr(HHI_MPLL_CNTL3, CFG_MPLL_CNTL_3); + //Wr(HHI_MPLL_CNTL4, CFG_MPLL_CNTL_4); + Wr(HHI_MPLL_CNTL5, CFG_MPLL_CNTL_5); + Wr(HHI_MPLL_CNTL6, CFG_MPLL_CNTL_6); + Wr(HHI_MPLL_CNTL, ((1 << 30) | (1<<29) | (3 << 9) | (250 << 0)) ); + Wr(HHI_MPLL_CNTL, Rd(HHI_MPLL_CNTL)&(~(1<<29))); //set reset bit to 0 + _udelay(800); + Wr(HHI_MPLL_CNTL4, Rd(HHI_MPLL_CNTL4)|(1<<14)); + do { + if ((Rd(HHI_MPLL_CNTL)&(1<<31)) != 0) + break; + Wr(HHI_MPLL_CNTL,Rd(HHI_MPLL_CNTL) | (1<<29)); + _udelay(1000); + Wr(HHI_MPLL_CNTL, Rd(HHI_MPLL_CNTL)&(~(1<<29))); + _udelay(1000); + }while(pll_lock_check(HHI_MPLL_CNTL, "FIX PLL")); + + // Enable the separate fclk_div2 and fclk_div3 + // .MPLL_CLK_OUT_DIV2_EN ( hi_mpll_cntl10[7:0] ), + // .MPLL_CLK_OUT_DIV3_EN ( hi_mpll_cntl10[11:8] ), + Wr( HHI_MPLL_CNTL10, (0xFFF << 16) ); + + // ------------------------------- + // Set Multi-Phase PLL0 = 350Mhz + // ------------------------------- + Wr( HHI_MPLL_CNTL7, ((7 << 16) | (1 << 15) | (1 << 14) | (4681 << 0)) ); + + // ------------------------- + // set CLK81 to 166.6Mhz Fixed + // ------------------------- + Wr( HHI_MPEG_CLK_CNTL, ((Rd(HHI_MPEG_CLK_CNTL) & (~((0x7 << 12) | (1 << 7) | (0x7F << 0)))) | ((5 << 12) | (1 << 7) | (2 << 0))) ); + // Connect clk81 to the PLL divider output + Wr( HHI_MPEG_CLK_CNTL, Rd(HHI_MPEG_CLK_CNTL) | (1 << 8) ); + + // ------------------------------- + // Set Multi-Phase PLL1 = 442.368 Mhz + // ------------------------------- + // +----------------------------------------+ + // | <<< Clock Reset Test >>> | + // +-------------------------------------+ +------+-----------+---------------------+ +------------ + // | Multi-Phase PLL | | CRT | Final | Ideal | | HIU Reg + // | FIn | N2 SDM_IN | CLKMP | | XD | Clock | Error Clock | | 0x10a7 + // +---------+--------------+------------| |------+-----------+---------------------+ +------------ + // | 24.0000 | 5 12524 | 442.3701 | | 1 | 442.3701 | 0.000% ( 442.368) | | 0x0005f0ec + // .MPLL_SDM_IN1 ( hi_mpll_cntl8[13:0] ), + // .MPLL_CH1_EN ( hi_mpll_cntl8[14] ), + // .MPLL_SDM_EN1 ( hi_mpll_cntl8[15] ), + // .MPLL_N_IN1 ( hi_mpll_cntl8[22:16] ), + // .MPLL_I160CTR1 ( hi_mpll_cntl8[25:24] ), + // .MPLL_R_SW1 ( hi_mpll_cntl8[27:26] ), + Wr( HHI_MPLL_CNTL8, ((5 << 16) | (1 << 15) | (1 << 14) | (12524 << 0)) ); + + return 0; +} + +// -------------------------------------------------- +// clocks_set_sys_cpu_clk +// -------------------------------------------------- +// This function sets the System CPU clock muxing and the +// sub-clocks related to the System CPU (AXI, PCLK,...) +// +// Parameters: +// freq: +// 0: 24Mhz Crystal +// 1: System PLL +// 1275, 850, 637,.... +// pclk_ratio: 0 = no change to the existing setting. 2,3,...8 = the clock ratio relative to the system CPU clock +// aclkm_ratio: 0 = no change to the existing setting. 2,3,...8 = the clock ratio relative to the system CPU clock +// atclk_ratio: 0 = no change to the existing setting. 2,3,...8 = the clock ratio relative to the system CPU clock +// -------------------------------- +// freq = 0: 24Mhz Crystal +// freq = 1: System PLL +// freq = 1000, 667, 500, 333, 250... +// Pass 0 to pclk_ratio or aclkm_ratio or atclk_ratio if nothing changes +void clocks_set_sys_cpu_clk(uint32_t freq, uint32_t pclk_ratio, uint32_t aclkm_ratio, uint32_t atclk_ratio ) +{ + uint32_t control = 0; + uint32_t dyn_pre_mux = 0; + uint32_t dyn_post_mux = 0; + uint32_t dyn_div = 0; + + // Make sure not busy from last setting and we currently match the last setting + do { + control = Rd(HHI_SYS_CPU_CLK_CNTL); + } while( (control & (1 << 28)) ); + + control = control | (1 << 26); // Enable + + // Switching to System PLL...just change the final mux + if ( freq == 1 ) { + // wire cntl_final_mux_sel = control[11]; + control = control | (1 << 11); + } else { + switch ( freq ) { + case 0: // If Crystal + dyn_pre_mux = 0; + dyn_post_mux = 0; + dyn_div = 0; // divide by 1 + break; + case 1000: // fclk_div2 + dyn_pre_mux = 1; + dyn_post_mux = 0; + dyn_div = 0; // divide by 1 + break; + case 667: // fclk_div3 + dyn_pre_mux = 2; + dyn_post_mux = 0; + dyn_div = 0; // divide by 1 + break; + case 500: // fclk_div2/2 + dyn_pre_mux = 1; + dyn_post_mux = 1; + dyn_div = 1; // Divide by 2 + break; + case 333: // fclk_div3/2 + dyn_pre_mux = 2; + dyn_post_mux = 1; + dyn_div = 1; // divide by 2 + break; + case 250: // fclk_div2/4 + dyn_pre_mux = 1; + dyn_post_mux = 1; + dyn_div = 3; // divide by 4 + break; + } + if ( control & (1 << 10) ) { // if using Dyn mux1, set dyn mux 0 + // Toggle bit[10] indicating a dynamic mux change + control = (control & ~((1 << 10) | (0x3f << 4) | (1 << 2) | (0x3 << 0))) + | ((0 << 10) + | (dyn_div << 4) + | (dyn_post_mux << 2) + | (dyn_pre_mux << 0)); + } else { + // Toggle bit[10] indicating a dynamic mux change + control = (control & ~((1 << 10) | (0x3f << 20) | (1 << 18) | (0x3 << 16))) + | ((1 << 10) + | (dyn_div << 20) + | (dyn_post_mux << 18) + | (dyn_pre_mux << 16)); + } + // Select Dynamic mux + control = control & ~(1 << 11); + } + Wr(HHI_SYS_CPU_CLK_CNTL,control); + // + // Now set the divided clocks related to the System CPU + // + // This function changes the clock ratios for the + // PCLK, ACLKM (AXI) and ATCLK + // .clk_clken0_i ( {clk_div2_en,clk_div2} ), + // .clk_clken1_i ( {clk_div3_en,clk_div3} ), + // .clk_clken2_i ( {clk_div4_en,clk_div4} ), + // .clk_clken3_i ( {clk_div5_en,clk_div5} ), + // .clk_clken4_i ( {clk_div6_en,clk_div6} ), + // .clk_clken5_i ( {clk_div7_en,clk_div7} ), + // .clk_clken6_i ( {clk_div8_en,clk_div8} ), + + uint32_t control1 = Rd(HHI_SYS_CPU_CLK_CNTL1); + + // .cntl_PCLK_mux ( hi_sys_cpu_clk_cntl1[5:3] ), + if ( (pclk_ratio >= 2) && (pclk_ratio <= 8) ) { control1 = (control1 & ~(0x7 << 3)) | ((pclk_ratio-2) << 3) ; } + // .cntl_ACLKM_clk_mux ( hi_sys_cpu_clk_cntl1[11:9] ), // AXI matrix + if ( (aclkm_ratio >= 2) && (aclkm_ratio <= 8) ) { control1 = (control1 & ~(0x7 << 9)) | ((aclkm_ratio-2) << 9) ; } + // .cntl_ATCLK_clk_mux ( hi_sys_cpu_clk_cntl1[8:6] ), + if ( (atclk_ratio >= 2) && (atclk_ratio <= 8) ) { control1 = (control1 & ~(0x7 << 6)) | ((atclk_ratio-2) << 6) ; } + Wr( HHI_SYS_CPU_CLK_CNTL1, control1 ); +} + +unsigned pll_lock_check(unsigned long pll_reg, const char *pll_name){ + /*locked: return 0, else return 1*/ + unsigned lock = ((Rd(pll_reg) >> PLL_LOCK_BIT_OFFSET) & 0x1); + if (lock) { + lock_check_loop = 0; + //serial_puts(pll_name); + //serial_puts("" lock ok!\n"); + } + else{ + lock_check_loop++; + serial_puts(pll_name); + serial_puts(" lock check "); + serial_put_dec(lock_check_loop); + serial_puts("\n"); + if (lock_check_loop >= PLL_lOCK_CHECK_LOOP) { + serial_puts(pll_name); + serial_puts(" lock failed! reset...\n"); + reset_system(); + while (1) ; + } + } + return !lock; +}
\ No newline at end of file |