| /************************************************************************************* |
| * Copyright (C) 2007-2011 |
| * Copyright ? 2007 Marvell International Ltd. |
| * |
| * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| * |
| ***************************************************************************************/ |
| |
| #include <linux/module.h> |
| #include <linux/version.h> |
| |
| #include "berlin_memmap.h" |
| #include "api_avpll.h" |
| #include "avioGbl.h" |
| |
| #define AVPLL_A (MEMMAP_AVIO_GBL_BASE + RA_avioGbl_AVPLLA) |
| #define PHY_HOST_Bus_Read32(addr_var, u32) *((volatile unsigned*)&(u32)) = *((volatile unsigned*)&(addr_var)) |
| #define PHY_HOST_Bus_Write32(addr_var, u32) *((volatile unsigned*)&(addr_var)) = (u32) |
| #define GA_REG_WORD32_READ(addr, holder) (*(holder) = (*((volatile unsigned int *)(addr)))) |
| #define GA_REG_WORD32_WRITE(addr, data) ((*((volatile unsigned int *)(addr))) = ((unsigned int)(data))) |
| |
| enum |
| { |
| VCO_FREQ_1_512G=0, |
| VCO_FREQ_1_620G, |
| VCO_FREQ_1_856G, |
| VCO_FREQ_2_160G, |
| VCO_FREQ_2_227G, |
| VCO_FREQ_2_520G, |
| VCO_FREQ_2_700G, |
| VCO_FREQ_2_970G, |
| }; |
| |
| typedef struct |
| { |
| int vco_freq_index; |
| float clkout_freq; //in MHz |
| int freq_offset; //signed number |
| unsigned int p_sync2; |
| unsigned int p_sync1; |
| unsigned int post_div; |
| unsigned int post_div_0p5; |
| unsigned int invalid; //this indicate it is a valid setting or not: 0 is valid, 1 is not |
| } CLK_FREQ; |
| |
| //all the VCO freq reqired for video and audio in MHz |
| double avpll_vcoFreqs[]= |
| { |
| 1512, //8 bit HDMI and 12 bit HMDI |
| 1620, //12 bit HDMI |
| 1856.25,//10 bit HDMI |
| 2160, //8 bit HDMI |
| 2227.5, //12 bit HDMI |
| 2520, //10 bit HDMI |
| 2700, //10 bit HDMI |
| 2970, //8 bit HDMI |
| }; |
| |
| //from Section 7 table |
| unsigned char avpll_avpllRegFBDIV[]= |
| { |
| 60, //VCO_FREQ_1_512G, |
| 65, //VCO_FREQ_1_620G, |
| 74, //VCO_FREQ_1_856G, |
| 86, //VCO_FREQ_2_160G, |
| 89, //VCO_FREQ_2_227G, |
| 101, //VCO_FREQ_2_520G, |
| 108, //VCO_FREQ_2_700G, |
| 119, //VCO_FREQ_2_970G, |
| }; |
| |
| //from Section 7 table, bit18 is sign bit |
| //Note that sign bit=0 for negative |
| //sign bit=1 for positive |
| unsigned int avpll_avpllRegFREQ_OFFSET_C8[]= |
| { |
| (33554), //VCO_FREQ_1_512G, |
| ((1<<18)|(12905)),//VCO_FREQ_1_620G, |
| (14169), //VCO_FREQ_1_856G, |
| (19508), //VCO_FREQ_2_160G, |
| (4712), //VCO_FREQ_2_227G |
| ((1<<18)|(8305)),//VCO_FREQ_2_520G, |
| (00000), //VCO_FREQ_2_700G, |
| ((1<<18)|(7049)),//VCO_FREQ_2_970G, |
| }; |
| |
| unsigned int avpll_avpllRegSPEED[]= |
| { |
| 0x2, //1.5G<F<=1.7G , for 1.512G |
| 0x2, //1.5G<F<=1.7G , for 1.62G |
| 0x3, //1.7G<F<=1.9G , for 1.856G |
| // 0x4, //1.9G<F<=2.1G |
| 0x5, //2.1G<F<=2.3G , for 2.16G |
| 0x5, //2.1G<F<=2.3G , for 2.227G |
| // 0x6, //2.3G<F<=2.45G |
| 0x7, //2.45G<F<=2.6G, for 2.52G |
| 0x8, //2.6G<F<= 2.75G, for 2.7G |
| // 0x9, //2.75G<F<= 2.9G |
| 0xa, //2.9G<F<=3.0G, for 2.97G |
| }; |
| |
| /*Interpolator current setting for different frequency |
| VCO Frequency ' INTPI |
| */ |
| unsigned int avpll_avpllRegINTPI[]= |
| { |
| 0x3, //for 1.512G |
| 0x3, //for 1.62G |
| 0x5, //for 1.856G |
| 0x7, //for 2.16G |
| 0x7, //for 2.227G |
| 0x9, //for 2.52G |
| 0xa, //for 2.7G |
| 0xb, //for 2.97G |
| }; |
| |
| unsigned int avpll_avpllRegINTPR[]= |
| { |
| 0x4, //for 1.512G |
| 0x4, //for 1.62G |
| 0x4, //for 1.856G |
| 0x3, //for 2.16G |
| 0x3, //for 2.227G |
| 0x3, //for 2.52G |
| 0x1, //for 2.7G |
| 0x1, //for 2.97G |
| }; |
| |
| volatile SIE_avPll regSIE_avPll; |
| int vcoFreqIndex = VCO_FREQ_2_160G; |
| int avpll_pll_A_VCO_Setting = VCO_FREQ_2_160G; |
| int avpll_pll_B_VCO_Setting = VCO_FREQ_1_620G; |
| static CLK_FREQ clk_freqs_computed[VCO_FREQ_2_970G+1]; |
| |
| /* PPM Adjustment methods */ |
| static int avpll_inited = 0; |
| static double org_ppm = 0.0; |
| static double cur_ppm = 0.0; |
| |
| #define OFFSET_SIZE 19 |
| #define ONE_MILLION 1000000.0 |
| #define OFFSET_MULTIPLIER_REFERENCE ((double)(1<<22)) |
| |
| static double offset_2_ppm(int offset) { |
| int v = offset & 0x3ffff; |
| if(offset & 0x40000) v = -v; |
| return -((ONE_MILLION * v)/(OFFSET_MULTIPLIER_REFERENCE + v)); |
| } |
| |
| // This is used to convert the real_ppm to nominal reference frequency to real base ref frequency. |
| // Fx/Fr = (1+ ppm_real) = Fx/f0*f0/Fr = (1+Offset_X/P)(1+ppm0) then Offset_X = P*(ppm_real-ppm0)/(1+ppm0) |
| // however the input parameter ppm = ppm0+ ppm_real(delta) so offset_X = P*(ppm -2*ppm0)/(1+ppm0) |
| static int HARD_LIMITER_19BITS(int x) |
| { |
| int max_limit = (1 << (OFFSET_SIZE - 1)) - 1; |
| int min_limit = ~(max_limit - 1); |
| return clamp_val(x, min_limit, max_limit); |
| } |
| |
| static int ppm_2_offset(double ppm, double ppm0) |
| { |
| double c = OFFSET_MULTIPLIER_REFERENCE * (ppm - 2*ppm0) / (ONE_MILLION + ppm0); |
| int offset = (int)c; |
| offset = HARD_LIMITER_19BITS(offset); |
| return (offset < 0) ? ((-offset) | 0x40000) : offset; |
| } |
| |
| static void cpu_cycle_count_delay(SIGN32 ns) |
| { |
| unsigned int start, diff, end; |
| |
| GA_REG_WORD32_READ(0xF7E82C04, &start); |
| |
| do { |
| GA_REG_WORD32_READ(0xF7E82C04, &end); |
| if(start >= end) |
| diff = start - end; |
| else |
| diff = 1000*1000 + start - end; |
| } while(diff < ns/10); |
| } |
| |
| static int avpll_gcd(unsigned int m, unsigned int n) |
| { |
| int rem; |
| while(n!=0) { |
| rem=m%n; |
| m=n; |
| n=rem; |
| } |
| |
| return(m); |
| } |
| |
| static int avpll_compute_freq_setting(unsigned int vco_freq_index, unsigned int target_freq) |
| { |
| double offset, offset_percent; |
| double divider; |
| double ratio; |
| int int_divider; |
| |
| memset(&(clk_freqs_computed[vco_freq_index]), 0, sizeof(CLK_FREQ)); |
| |
| clk_freqs_computed[vco_freq_index].vco_freq_index=vco_freq_index; |
| clk_freqs_computed[vco_freq_index].clkout_freq=(float)target_freq/1000000; |
| |
| //.5 divider is only used when divider is less than 24 |
| //This matches the settings in audio freq table in the IP doc |
| ratio = avpll_vcoFreqs[vco_freq_index]*1000000/target_freq; |
| |
| if(ratio < 24) { |
| //allow 0.5 divider, round to closest 0.5 |
| int_divider = (int)(ratio*2+0.5); |
| } else { |
| //round to closest int |
| int_divider = ((int)(ratio+0.5))*2; |
| } |
| |
| divider = ((double)int_divider)/2; |
| |
| clk_freqs_computed[vco_freq_index].post_div_0p5= (int_divider&1); |
| clk_freqs_computed[vco_freq_index].post_div=((int)(divider)); |
| |
| //now figure out the offset |
| offset_percent = (target_freq*divider/1000000 - avpll_vcoFreqs[vco_freq_index])/avpll_vcoFreqs[vco_freq_index]; |
| |
| offset = 4194304*offset_percent/(1+offset_percent); |
| |
| if(offset<=-262144 || offset>= 262144) { |
| //offset cannot be achieved |
| clk_freqs_computed[vco_freq_index].invalid = 1; |
| return 1; |
| } else { |
| unsigned int vco_ratio, freq_ratio, gcd_val; |
| |
| //for rounding |
| if(offset>0) offset+=0.5; |
| else offset-=0.5; |
| |
| clk_freqs_computed[vco_freq_index].freq_offset = (int)(offset); |
| gcd_val= avpll_gcd(avpll_vcoFreqs[vco_freq_index]*1000000, target_freq*divider); |
| vco_ratio = (int)(avpll_vcoFreqs[vco_freq_index]*1000000/gcd_val); |
| freq_ratio = (int)(target_freq*divider/gcd_val); |
| |
| if((gcd_val/1000)<1000) { |
| clk_freqs_computed[vco_freq_index].p_sync2=freq_ratio; |
| clk_freqs_computed[vco_freq_index].p_sync1=vco_ratio; |
| } else { |
| clk_freqs_computed[vco_freq_index].p_sync2=freq_ratio*((int)(gcd_val/1000000+0.5)); |
| clk_freqs_computed[vco_freq_index].p_sync1=vco_ratio*((int)(gcd_val/1000000+0.5)); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void avpll_powerDownChannel(SIE_avPll *avPllBase, int chId) |
| { |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_PU = 0; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| } |
| |
| static void avpll_powerUpChannel(SIE_avPll *avPllBase, int chId) |
| { |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_PU = 1; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| } |
| |
| static void avpll_powerDown(SIE_avPll *avPllBase) |
| { |
| int chId; |
| |
| //avPllBase->uctrlPLL_PU = 0; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_PU = 0; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| |
| for(chId=1; chId<=7; chId++) { |
| avpll_powerDownChannel(avPllBase, chId); |
| } |
| |
| //avPllBase->ie_C8.uctrl_PU = 0; |
| PHY_HOST_Bus_Read32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| (regSIE_avPll.ie_C8).uctrl_PU = 0; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| } |
| |
| static void avpll_powerUp(SIE_avPll *avPllBase) |
| { |
| int chId; |
| |
| for(chId=1; chId<=7; chId++) { |
| avpll_powerUpChannel(avPllBase, chId); |
| } |
| |
| //avPllBase->ie_C8.uctrl_PU = 1; |
| PHY_HOST_Bus_Read32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| (regSIE_avPll.ie_C8).uctrl_PU = 1; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| |
| //avPllBase->uctrlPLL_PU = 1; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_PU = 1; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| } |
| |
| static void avpll_assertPllReset(SIE_avPll *avPllBase) |
| { |
| //assert reset |
| //avPllBase->uctrlPLL_RESET=1; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_RESET = 1; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| } |
| |
| //assert reset for channel1 to 7 |
| static void avpll_assertCxReset(SIE_avPll *avPllBase) |
| { |
| int chId; |
| |
| for(chId=1; chId<=7; chId++) { |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_RESET = 1; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl3, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl3); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_RESET = 1; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl3, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl3); |
| } |
| } |
| |
| static void avpll_setVDDL_VCOref(SIE_avPll *avPllBase) |
| { |
| //T32avPll_ctrlPLL ctrlPLL; |
| //ctrlPLL.u32 = avPllBase->u32avPll_ctrlPLL; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| |
| //ctrlPLL.uctrlPLL_VCO_REF1P45_SEL = 0x3; |
| //ctrlPLL.uctrlPLL_VDDL = 0xF;// set VDDL to 1.16v |
| regSIE_avPll.uctrlPLL_VCO_REF1P45_SEL = 0x3; |
| regSIE_avPll.uctrlPLL_VDDL = 0xF;// set VDDL to 1.16v |
| |
| //* (volatile unsigned int *)(&(avPllBase->u32avPll_ctrlPLL)) = ctrlPLL.u32; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| } |
| |
| static void avpll_deassertPllReset(SIE_avPll *avPllBase) |
| { |
| volatile int i; |
| |
| for(i=0; i<10000*10; i++); |
| |
| //deassert reset |
| //avPllBase->uctrlPLL_RESET=0; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_RESET = 0; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| } |
| |
| //assert reset for channel1 to 7 |
| static void avpll_deassertCxReset(SIE_avPll *avPllBase) |
| { |
| int chId; |
| |
| for(chId=1; chId<=7; chId++) { |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_RESET = 0; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl3, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl3); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_RESET = 0; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl3, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl3); |
| } |
| } |
| |
| static void avpll_calibrate(SIE_avPll *avPllBase, double fvco) |
| { |
| volatile int i=100000; |
| //MV_TimeSpec_t Time_Start, Time_End; |
| |
| //set the speed_thresh for current Fvco |
| //avPllBase->uctrlCAL_SPEED_THRESH = (int)(fvco/100+0.5); |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlCAL, regSIE_avPll.u32avPll_ctrlCAL); |
| regSIE_avPll.uctrlCAL_SPEED_THRESH = (int)(fvco/100+0.5); |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlCAL, regSIE_avPll.u32avPll_ctrlCAL); |
| |
| //assert PLL resebratet |
| //avPllBase->uctrlPLL_RESET=1; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_RESET = 1; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| |
| cpu_cycle_count_delay(20000); |
| |
| //MV_Time_GetSysTime(&Time_Start); |
| //avPllBase->uctrlPLL_RESET=0; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_RESET = 0; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| |
| //>20us and <50us must start calibration |
| cpu_cycle_count_delay(35000); |
| |
| //avPllBase->uctrlCAL_PLL_CAL_START = 1; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlCAL, regSIE_avPll.u32avPll_ctrlCAL); |
| regSIE_avPll.uctrlCAL_PLL_CAL_START = 1; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlCAL, regSIE_avPll.u32avPll_ctrlCAL); |
| |
| //poll PLL CAL done bit |
| //while(!avPllBase->ustatus_PLL_CAL_DONE) |
| while(1) { |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_status, regSIE_avPll.u32avPll_status); |
| if(regSIE_avPll.ustatus_PLL_CAL_DONE) |
| break; |
| |
| if(--i<0) { |
| break; |
| } |
| } |
| |
| cpu_cycle_count_delay(20000); |
| |
| //avPllBase->uctrlCAL_PLL_CAL_START = 0; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlCAL, regSIE_avPll.u32avPll_ctrlCAL); |
| regSIE_avPll.uctrlCAL_PLL_CAL_START = 0; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlCAL, regSIE_avPll.u32avPll_ctrlCAL); |
| } |
| |
| static void avpll_setVCO(SIE_avPll *avPllBase, int vco_freq_index) |
| { |
| //first power done PLL and Channels |
| avpll_powerDown(avPllBase); |
| |
| //assert resets |
| avpll_assertPllReset(avPllBase); |
| avpll_assertCxReset(avPllBase); |
| |
| //change VDDL and VCO_ref to meet duty cycle |
| avpll_setVDDL_VCOref(avPllBase); |
| |
| //power up PLL and channels |
| avpll_powerUp(avPllBase); |
| |
| // @yeliu: power down these channels by hardcoded, only for bg2cdp |
| avpll_powerDownChannel(avPllBase, 5); |
| avpll_powerDownChannel(avPllBase, 6); |
| |
| //following settings are done under reset to improve long term reliability |
| //avPllBase->uctrlPLL_FBDIV = avpll_avpllRegFBDIV[vco_freq_index]; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| regSIE_avPll.uctrlPLL_FBDIV = avpll_avpllRegFBDIV[vco_freq_index]; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL, regSIE_avPll.u32avPll_ctrlPLL); |
| |
| //avPllBase->uctrlINTP_INTPI = avpll_avpllRegINTPI[vco_freq_index]; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlINTP, regSIE_avPll.u32avPll_ctrlINTP); |
| regSIE_avPll.uctrlINTP_INTPI = avpll_avpllRegINTPI[vco_freq_index]; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlINTP, regSIE_avPll.u32avPll_ctrlINTP); |
| |
| //avPllBase->uctrlINTP_INTPR = avpll_avpllRegINTPR[vco_freq_index]; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlINTP, regSIE_avPll.u32avPll_ctrlINTP); |
| regSIE_avPll.uctrlINTP_INTPR = avpll_avpllRegINTPR[vco_freq_index]; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlINTP, regSIE_avPll.u32avPll_ctrlINTP); |
| |
| //avPllBase->uctrlPLL_EXT_SPEED = avpll_avpllRegSPEED[vco_freq_index]; |
| PHY_HOST_Bus_Read32(avPllBase->u32avPll_ctrlPLL1, regSIE_avPll.u32avPll_ctrlPLL1); |
| regSIE_avPll.uctrlPLL_EXT_SPEED = avpll_avpllRegSPEED[vco_freq_index]; |
| PHY_HOST_Bus_Write32(avPllBase->u32avPll_ctrlPLL1, regSIE_avPll.u32avPll_ctrlPLL1); |
| |
| //(avPllBase->ie_C8).uctrl_FREQ_OFFSET = avpll_avpllRegFREQ_OFFSET_C8[vco_freq_index]; |
| PHY_HOST_Bus_Read32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| (regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET = avpll_avpllRegFREQ_OFFSET_C8[vco_freq_index]; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| |
| if(avPllBase == AVPLL_A) |
| avpll_pll_A_VCO_Setting = vco_freq_index; |
| else |
| avpll_pll_B_VCO_Setting = vco_freq_index; |
| |
| //deassert resets |
| avpll_deassertCxReset(avPllBase); |
| |
| avpll_deassertPllReset(avPllBase); |
| |
| //toggle the offset_rdy bit |
| //(avPllBase->ie_C8).uctrl_FREQ_OFFSET_READY = 1; |
| PHY_HOST_Bus_Read32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| (regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET_READY = 1; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| |
| //add some delay for Fvco=3GHz pulse>172ns, For Fvco=1.5GHz pulse>344ns |
| cpu_cycle_count_delay(550); |
| |
| //(avPllBase->ie_C8).uctrl_FREQ_OFFSET_READY = 0; |
| PHY_HOST_Bus_Read32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| (regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET_READY = 0; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| |
| //do calibration |
| avpll_calibrate(avPllBase, avpll_vcoFreqs[vco_freq_index]); |
| } |
| |
| static void avpll_setChanOffset(SIE_avPll *avPllBase, int offset, int chId) |
| { |
| unsigned int reg_offset = 0; |
| |
| if(offset>0) { |
| reg_offset = (1<<18) | (offset) ; |
| } else { |
| reg_offset = -offset; |
| } |
| |
| //set offset register |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_FREQ_OFFSET = reg_offset; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_FREQ_OFFSET = reg_offset; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| |
| //toggle the offset_rdy bit |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_FREQ_OFFSET_READY = 1; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_FREQ_OFFSET_READY = 1; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| |
| //add some delay because need to be asserted by 30ns |
| cpu_cycle_count_delay(200); |
| |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_FREQ_OFFSET_READY = 0; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_FREQ_OFFSET_READY = 0; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl1, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl1); |
| } |
| |
| static void avpll_setDPll(SIE_avPll *avPllBase, int enable, int p_sync1, int p_sync2, int chId) |
| { |
| //disable DPll first |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_EN_DPLL = 0; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_EN_DPLL = 0; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_P_SYNC1 = p_sync1; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl2, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl2); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_P_SYNC1 = p_sync1; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl2, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl2); |
| |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_P_SYNC2 = p_sync2; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl3, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl3); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_P_SYNC2 = p_sync2; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl3, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl3); |
| |
| if(enable) { |
| //enable DPLL |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_EN_DPLL = 1; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_EN_DPLL = 1; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| } |
| } |
| |
| static void avpll_set_Post_Div(SIE_avPll *avPllBase, int div, int chId) |
| { |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_POSTDIV = div; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_POSTDIV = div; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| } |
| |
| static void avpll_set_Post_0P5_Div(SIE_avPll *avPllBase, int enable, int chId) |
| { |
| //((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].uctrl_POSTDIV_0P5 = enable; |
| PHY_HOST_Bus_Read32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].uctrl_POSTDIV_0P5 = enable; |
| PHY_HOST_Bus_Write32(((SIE_avpllCh *)(&(avPllBase->ie_C1)))[chId-1].u32avpllCh_ctrl, ((SIE_avpllCh *)&(regSIE_avPll.ie_C1))[chId-1].u32avpllCh_ctrl); |
| } |
| |
| static int avpll_clockFreq_computed(SIE_avPll *avPllBase, int freqIndex, int chId) |
| { |
| int avpll_pll_VCO_Setting; |
| |
| if(avPllBase == AVPLL_A) { |
| avpll_pll_VCO_Setting=avpll_pll_A_VCO_Setting; |
| } else { |
| avpll_pll_VCO_Setting=avpll_pll_B_VCO_Setting; |
| } |
| |
| if(freqIndex > (sizeof(clk_freqs_computed) / sizeof(CLK_FREQ))) { |
| return 1; |
| } |
| |
| if(clk_freqs_computed[freqIndex].invalid) { |
| return 1; |
| } |
| |
| if(avpll_pll_VCO_Setting != clk_freqs_computed[freqIndex].vco_freq_index) { |
| avpll_setVCO(avPllBase, clk_freqs_computed[freqIndex].vco_freq_index); |
| } |
| |
| //change offset |
| avpll_setChanOffset(avPllBase, clk_freqs_computed[freqIndex].freq_offset, chId); |
| |
| //change p_sync |
| avpll_setDPll(avPllBase, (clk_freqs_computed[freqIndex].p_sync1!=0), |
| clk_freqs_computed[freqIndex].p_sync1, |
| clk_freqs_computed[freqIndex].p_sync2, chId); |
| |
| //update now div |
| avpll_set_Post_Div(avPllBase, clk_freqs_computed[freqIndex].post_div, chId); |
| avpll_set_Post_0P5_Div(avPllBase, clk_freqs_computed[freqIndex].post_div_0p5, chId); |
| |
| return 0; |
| } |
| |
| static int avpll_clockFreq(SIE_avPll *avPllBase, int vco_freq_index, unsigned int target_freq, int chId) |
| { |
| if(avpll_compute_freq_setting(vco_freq_index, target_freq)) { |
| return 1; |
| } else { |
| //frequency ok, set it |
| avpll_clockFreq_computed(avPllBase, vco_freq_index, chId); |
| } |
| |
| return 0; |
| } |
| |
| int AVPLL_Set(int grpId, int chanId, unsigned int avFreq) |
| { |
| if(grpId == 0) |
| avpll_clockFreq(AVPLL_A, vcoFreqIndex, avFreq, chanId); |
| |
| return 0; |
| } |
| |
| static void avpll_get_ppm(double *ppm_base, double *ppm_now) |
| { |
| if(ppm_base) *ppm_base = org_ppm; |
| if(ppm_now) *ppm_now = cur_ppm; |
| } |
| |
| static double avpll_adjust_ppm(double ppm_delta) |
| { |
| double ppm0, ppm; |
| SIE_avPll *avPllBase; |
| |
| avPllBase = (SIE_avPll *)AVPLL_A; |
| PHY_HOST_Bus_Read32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| if (avpll_inited == 0) { |
| ppm0 = offset_2_ppm((regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET); |
| cur_ppm = org_ppm = ppm0; |
| avpll_inited = 1; |
| } |
| else { |
| ppm0 = org_ppm; |
| } |
| cur_ppm += ppm_delta; |
| ppm = cur_ppm; |
| |
| (regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET = ppm_2_offset(ppm, org_ppm); |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| //toggle the offset_rdy bit |
| (regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET_READY = 1; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| //add some delay for Fvco=3GHz pulse>172ns, For Fvco=1.5GHz pulse>344ns |
| cpu_cycle_count_delay(1000); |
| (regSIE_avPll.ie_C8).uctrl_FREQ_OFFSET_READY = 0; |
| PHY_HOST_Bus_Write32((avPllBase->ie_C8).u32avpllCh8_ctrl1, (regSIE_avPll.ie_C8).u32avpllCh8_ctrl1); |
| return ppm - ppm0; |
| } |
| |
| double AVPLL_AdjustPPM(double ppm_delta) |
| { |
| return avpll_adjust_ppm(ppm_delta); |
| } |
| |
| void AVPLL_GetPPM(double *ppm_base, double *ppm_now) |
| { |
| avpll_get_ppm(ppm_base, ppm_now); |
| } |