blackhawk_pll_config.c (16246B)
1 /********************************************************************************** 2 ********************************************************************************** 3 * File Name : blackhawk_pll_config.c * 4 * Created On : 11 Feb 2016 * 5 * Created By : Brent Roberts * 6 * Description : Blackhawk PLL Configuration API * 7 * Revision : * 8 * * 9 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 10 * 11 * Copyright 2007-2019 Broadcom Inc. All rights reserved. * 12 * No portions of this material may be reproduced in any form without * 13 * the written permission of: * 14 * Broadcom Corporation * 15 * 5300 California Avenue * 16 * Irvine, CA 92617 * 17 * * 18 * All information contained in this document is Broadcom Corporation * 19 * company private proprietary, and trade secret. * 20 */ 21 22 23 /** @file blackhawk_pll_config.c 24 * Blackhawk PLL Configuration 25 */ 26 27 28 #include "blackhawk_tsc_config.h" 29 #include "blackhawk_tsc_functions.h" 30 #include "blackhawk_tsc_internal.h" 31 #include "blackhawk_tsc_internal_error.h" 32 #include "blackhawk_tsc_select_defns.h" 33 34 35 #define VCO_FREQ_KHZ_NEAR(vco_freq_khz_, near_val_) ( ((vco_freq_khz_) >= (near_val_) - 10000) \ 36 || ((vco_freq_khz_) <= (near_val_) + 10000)) 37 38 /* The pll_fracn_ndiv_int and pll_fracn_frac bitfields have this many bits. */ 39 static const uint32_t pll_fracn_ndiv_int_bits = 10; 40 static const uint32_t pll_fracn_frac_bits = 18; 41 42 err_code_t blackhawk_tsc_INTERNAL_configure_pll(srds_access_t *sa__, 43 enum blackhawk_tsc_pll_refclk_enum refclk, 44 enum blackhawk_tsc_pll_div_enum srds_div, 45 uint32_t vco_freq_khz, 46 enum blackhawk_tsc_pll_option_enum pll_option) { 47 uint32_t refclk_freq_hz; 48 uint8_t int_configured; 49 50 EFUN(blackhawk_tsc_INTERNAL_resolve_pll_parameters(sa__, refclk, &refclk_freq_hz, &srds_div, &vco_freq_khz, pll_option)); 51 52 53 /* Use this to restore defaults if reprogramming the PLL under dp-reset (typically Auto-Neg FW) */ 54 EFUN(wrc_pll_mode(0x7)); 55 EFUN(wrc_ams_pll_vco2_15g(0x0)); 56 EFUN(wrc_ams_pll_fracn_ndiv_int(0x0)); 57 EFUN(wrc_ams_pll_fracn_div(0x0000)); 58 EFUN(wrc_ams_pll_fracn_div_17_16(0x0)); 59 EFUN(wrc_ams_pll_fracn_bypass(0x0)); 60 EFUN(wrc_ams_pll_fracn_divrange(0x0)); 61 EFUN(wrc_ams_pll_ditheren(0x0)); 62 EFUN(wrc_ams_pll_fracn_sel(0x0)); 63 EFUN(wrc_ams_pll_kvh_force(0x0)); 64 EFUN(wrc_ams_pll_force_kvh_bw(0x0)); 65 EFUN(wrc_ams_pll_refclk_doubler(0x0)); 66 EFUN(wrc_ams_pll_doubler_res(0x0)); 67 EFUN(wrc_ams_pll_doubler_cap(0x0)); 68 EFUN(wrc_ams_pll_div2(0x0)); 69 EFUN(wrc_ams_pll_div4(0x0)); 70 EFUN(wrc_ams_pll_div4_2_sel(0x0)); 71 EFUN(wrc_ams_pll_ndiv_frac_valid(0x0)); 72 EFUN(wrc_ams_pll_pll2rx_clkbw(0x0)); 73 EFUN(wrc_refclk_divcnt(0x27)); 74 EFUN(wrc_vco_step_time(0x3)); 75 EFUN(wrc_ams_pll_fp3_rh(0x0)); 76 EFUN(wrc_ams_pll_fp3_ctrl(0x0)); 77 EFUN(wrc_ams_pll_iqp(0x4)); 78 79 { 80 uint8_t reset_state; 81 /* Use core_s_rstb to re-initialize all registers to default before calling this function. */ 82 ESTM(reset_state = rdc_core_dp_reset_state()); 83 84 if(reset_state < 7) { 85 EFUN_PRINTF(("ERROR: blackhawk_tsc_configure_pll(..) called without core_dp_s_rstb=0\n")); 86 return (blackhawk_tsc_error(sa__, ERR_CODE_CORE_DP_NOT_RESET)); 87 } 88 } 89 90 /* Clear PLL powerdown */ 91 EFUN(wrc_ams_pll_pwrdn(0)); 92 93 /* Per AB on 13 Nov 2015: Use VCO2 for 22.6 GHz and below. 94 * The next supported frequency is 23.0 GHz, so let's use 22.8 GHz as the threshold. 95 */ 96 EFUN(wrc_ams_pll_vco2_15g((vco_freq_khz < 22800000) ? 1 : 0)); 97 98 int_configured = 0; 99 if (!SRDS_INTERNAL_IS_PLL_DIV_FRACTIONAL(srds_div)) { 100 uint8_t pll_mode; 101 int_configured = 1; 102 switch (SRDS_INTERNAL_GET_PLL_DIV_INTEGER(srds_div)) { 103 case 64: pll_mode = 0; break; 104 case 66: pll_mode = 1; break; 105 case 80: pll_mode = 2; break; 106 case 128: pll_mode = 3; break; 107 case 132: pll_mode = 4; break; 108 case 140: pll_mode = 5; break; 109 case 160: pll_mode = 6; break; 110 case 165: pll_mode = 7; break; 111 case 168: pll_mode = 8; break; 112 case 170: pll_mode = 9; break; 113 case 175: pll_mode = 10; break; 114 case 180: pll_mode = 11; break; 115 case 184: pll_mode = 12; break; 116 case 200: pll_mode = 13; break; 117 case 224: pll_mode = 14; break; 118 case 264: pll_mode = 15; break; 119 case 96: pll_mode = 16; break; 120 case 120: pll_mode = 17; break; 121 case 144: pll_mode = 18; break; 122 case 198: pll_mode = 19; break; 123 default: int_configured = 0; 124 } 125 126 if (int_configured) { 127 EFUN(wrc_pll_mode(pll_mode)); 128 } 129 } 130 131 EFUN(wrc_ams_pll_fracn_sel(int_configured ? 0 : 1)); 132 133 if (!int_configured) { 134 /* Either fractional mode was requested, or integer mode doesn't support the divisor. 135 * Get information needed for fractional mode configuration. 136 * 137 * The value programmed into the pll_fracn_* bitfields which must account for the 138 * initial div2 stage after the VCO. For instance, a divide by 147.2 must be 139 * programmed with an integer of 73 and a fraction of 0.6. 140 * 141 * Start with the div value, divided by 2, composed of an integer and a wide fractional value. 142 */ 143 const uint8_t div_fraction_width = 28; /* Must be less than 32 due to overflow detection below. */ 144 const uint16_t div_integer = SRDS_INTERNAL_GET_PLL_DIV_INTEGER(srds_div) >> 1; 145 const uint32_t div_fraction = (((SRDS_INTERNAL_GET_PLL_DIV_INTEGER(srds_div) & 1) << (div_fraction_width-1)) 146 | SRDS_INTERNAL_GET_PLL_DIV_FRACTION_NUM(srds_div, div_fraction_width-1)); 147 148 /* The div_fraction may have more precision than our pll_fracn_frac bitfield. 149 * So round it. Start by adding 1/2 LSB of the fraction div_fraction. 150 */ 151 const uint32_t div_fraction_0p5 = 1 << (div_fraction_width - pll_fracn_frac_bits - 1); 152 const uint32_t div_fraction_plus_0p5 = div_fraction + div_fraction_0p5; 153 154 /* Did div_fraction_plus_p5 have a carry bit? */ 155 const uint32_t div_fraction_plus_p5_carry = div_fraction_plus_0p5 >> div_fraction_width; 156 157 /* The final rounded div_fraction, including carry up to div_integer. 158 * This removes the carry and implements the fixed point truncation. 159 */ 160 const uint16_t pll_fracn_ndiv_int = div_integer + div_fraction_plus_p5_carry; 161 const uint32_t pll_fracn_div = ((div_fraction_plus_0p5 & ((1 << div_fraction_width)-1)) 162 >> (div_fraction_width - pll_fracn_frac_bits)); 163 164 if (pll_fracn_ndiv_int != (pll_fracn_ndiv_int & ((1 << pll_fracn_ndiv_int_bits)-1))) { 165 EFUN_PRINTF(("ERROR: PLL divide is too large for div value 0x%08X\n", srds_div)); 166 return (blackhawk_tsc_error(sa__, ERR_CODE_PLL_DIV_INVALID)); 167 } 168 169 EFUN(wrc_pll_mode(0)); 170 if ((pll_fracn_ndiv_int < 12) || (pll_fracn_ndiv_int > 251)) { 171 return (blackhawk_tsc_error(sa__, ERR_CODE_INVALID_PLL_CFG)); 172 } 173 EFUN(wrc_ams_pll_fracn_ndiv_int (pll_fracn_ndiv_int)); 174 EFUN(wrc_ams_pll_fracn_div (pll_fracn_div & 0xFFFF)); 175 EFUN(wrc_ams_pll_fracn_div_17_16(pll_fracn_div >> 16)); 176 EFUN(wrc_ams_pll_fracn_bypass (0)); 177 EFUN(wrc_ams_pll_fracn_divrange ((pll_fracn_ndiv_int < 91) ? 1 : 0)); 178 if (pll_fracn_div != 0) { 179 /* Apply settings on PLL0 to avoid coupling related jitter. See CRSRDSAPI-308. */ 180 if ((refclk_freq_hz > 300000000) && (blackhawk_tsc_get_pll_idx(sa__) == 0)) { 181 EFUN(wrc_ams_pll_fp3_rh(0x0)); 182 EFUN(wrc_ams_pll_fp3_ctrl(0x0)); 183 EFUN(wrc_ams_pll_iqp(0x4)); 184 } else if ((refclk_freq_hz > 300000000) && (blackhawk_tsc_get_pll_idx(sa__) == 1)) { 185 EFUN(wrc_ams_pll_fp3_rh(0x0)); 186 EFUN(wrc_ams_pll_fp3_ctrl(0xF)); 187 EFUN(wrc_ams_pll_iqp(0x6)); 188 } else 189 { 190 EFUN(wrc_ams_pll_fp3_rh(0x1)); 191 EFUN(wrc_ams_pll_fp3_ctrl(0xF)); 192 } 193 } 194 /* toggle ndiv_frac_valid high, then low to load in a new value for fracn_div. */ 195 EFUN(wrc_ams_pll_ndiv_frac_valid(1)); 196 EFUN(wrc_ams_pll_ndiv_frac_valid(0)); 197 } 198 199 { 200 uint8_t ams_tx_version_id; 201 ESTM(ams_tx_version_id = rd_ams_tx_version_id()); 202 if ((ams_tx_version_id == 0xC0) && (refclk_freq_hz == 156250000) && (vco_freq_khz == 26562500)) { 203 EFUN(wrc_ams_pll_iqp(0x8)); 204 } 205 } 206 207 /* Determine KVH and pll2rx_clkbw_val per Falcon16 AMS v1.1 and comments in CRSRDSAPI-137 and CRSRDSFW-138 */ 208 { 209 uint8_t kvh_force; 210 uint8_t pll2rx_clkbw_val; 211 212 if (vco_freq_khz > 27400000) { 213 kvh_force = 0; 214 } else if ((vco_freq_khz <= 27400000) && (vco_freq_khz > 18750000)) { 215 kvh_force = 1; 216 } else { 217 kvh_force = 3; 218 } 219 220 if (vco_freq_khz > 25781250) { 221 pll2rx_clkbw_val = 0; 222 } else if ((vco_freq_khz <= 25781250) && (vco_freq_khz > 22600000)) { 223 pll2rx_clkbw_val = 1; 224 } else { 225 pll2rx_clkbw_val = 3; 226 } 227 228 /* Per AB on 27 Oct 2015, vco_indicator is always forced to 0. */ 229 #if defined(wrc_ams_pll_vco_indicator) 230 EFUN(wrc_ams_pll_vco_indicator(0)); 231 #endif 232 EFUN(wrc_ams_pll_pll2rx_clkbw(pll2rx_clkbw_val)); 233 EFUN(wrc_ams_pll_kvh_force(kvh_force)); 234 EFUN(wrc_ams_pll_force_kvh_bw(1)); 235 236 /* Handle refclk PLL options */ 237 pll_option = (enum blackhawk_tsc_pll_option_enum)(pll_option & BLACKHAWK_TSC_PLL_OPTION_REFCLK_MASK); 238 if (pll_option == BLACKHAWK_TSC_PLL_OPTION_REFCLK_DOUBLER_EN) { 239 EFUN(wrc_ams_pll_refclk_doubler(0x1)); 240 EFUN(wrc_ams_pll_doubler_res(0x6)); 241 EFUN(wrc_ams_pll_doubler_cap(0x3)); 242 } else if (pll_option == BLACKHAWK_TSC_PLL_OPTION_REFCLK_DIV2_EN) { 243 EFUN(wrc_ams_pll_div2(1)); 244 EFUN(wrc_ams_pll_div4_2_sel(1)); 245 } else if (pll_option == BLACKHAWK_TSC_PLL_OPTION_REFCLK_DIV4_EN) { 246 EFUN(wrc_ams_pll_div4(1)); 247 EFUN(wrc_ams_pll_div4_2_sel(1)); 248 } 249 250 251 /* Force refclk_divcnt for refclk faster than 156.25 Mhz. See CRSRDSAPI-185. */ 252 if (refclk_freq_hz > 156250000) { 253 uint16_t refclk_divcnt = 39 * (refclk_freq_hz/156250000); 254 EFUN(wrc_refclk_divcnt(refclk_divcnt)); 255 } 256 257 /* Force vco_step_time. See CRSRDSAPI-229. */ 258 EFUN(wrc_vco_step_time(8)); 259 } 260 261 /* Update core variables with the VCO rate. */ 262 { 263 struct blackhawk_tsc_uc_core_config_st core_config; 264 EFUN(blackhawk_tsc_get_uc_core_config(sa__, &core_config)); 265 core_config.vco_rate_in_Mhz = (vco_freq_khz + 500) / 1000; 266 core_config.field.vco_rate = MHZ_TO_VCO_RATE(core_config.vco_rate_in_Mhz); 267 EFUN(blackhawk_tsc_INTERNAL_set_uc_core_config(sa__, core_config)); 268 } 269 270 { 271 uint8_t pll_idx; 272 uint8_t other_pll_lock; 273 ESTM(pll_idx = blackhawk_tsc_get_pll_idx(sa__)); 274 EFUN(blackhawk_tsc_set_pll_idx(sa__, ((pll_idx == 0) ? 1 : 0))); 275 ESTM(other_pll_lock = rdc_pll_lock()); 276 if (!other_pll_lock) { 277 uint8_t misc_ctrl_byte; 278 ESTM(misc_ctrl_byte = rdcv_misc_ctrl_byte()); 279 if (pll_idx == 1) misc_ctrl_byte |= 1<<2; 280 else misc_ctrl_byte &= ~(1<<2); 281 EFUN(wrcv_misc_ctrl_byte(misc_ctrl_byte)); 282 } 283 EFUN(blackhawk_tsc_set_pll_idx(sa__, pll_idx)); 284 } 285 286 return (ERR_CODE_NONE); 287 } /* blackhawk_tsc_configure_pll */ 288 289 err_code_t blackhawk_tsc_INTERNAL_read_pll_div(srds_access_t *sa__, uint32_t *srds_div) { 290 uint8_t fracn_sel; 291 ESTM(fracn_sel = rdc_ams_pll_fracn_sel()); 292 293 if (fracn_sel) { 294 uint16_t pll_fracn_ndiv_int; 295 uint32_t pll_fracn_div; 296 ESTM(pll_fracn_ndiv_int = rdc_ams_pll_fracn_ndiv_int()); 297 ESTM(pll_fracn_div = rdc_ams_pll_fracn_div() | (rdc_ams_pll_fracn_div_17_16() << 16)); 298 299 /* The value programmed into the pll_fracn_* bitfields which must 300 * account for the initial div2 stage after the VCO. For instance, a 301 * divide by 147.2 must be programmed with an integer of 73 and a 302 * fraction of 0.6. 303 * 304 * So multiply the bitfield reads by 2. 305 */ 306 pll_fracn_ndiv_int <<= 1; 307 pll_fracn_div <<= 1; 308 309 { 310 /* If the post-multiplied fractional part overflows, then apply the carry to 311 * the integer part. 312 */ 313 const uint32_t pll_fracn_div_masked = pll_fracn_div & ((1 << pll_fracn_frac_bits)-1); 314 if (pll_fracn_div_masked != pll_fracn_div) { 315 ++pll_fracn_ndiv_int; 316 pll_fracn_div = pll_fracn_div_masked; 317 } 318 } 319 320 *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV(pll_fracn_ndiv_int, pll_fracn_div, pll_fracn_frac_bits); 321 } else { 322 uint8_t pll_mode; 323 ESTM(pll_mode = rdc_pll_mode()); 324 switch (pll_mode) { 325 case 0: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 64, 0, 18); break; 326 case 1: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 66, 0, 18); break; 327 case 2: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 80, 0, 18); break; 328 case 3: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 128, 0, 18); break; 329 case 4: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 132, 0, 18); break; 330 case 5: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 140, 0, 18); break; 331 case 6: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 160, 0, 18); break; 332 case 7: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 165, 0, 18); break; 333 case 8: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 168, 0, 18); break; 334 case 9: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 170, 0, 18); break; 335 case 10: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 175, 0, 18); break; 336 case 11: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 180, 0, 18); break; 337 case 12: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 184, 0, 18); break; 338 case 13: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 200, 0, 18); break; 339 case 14: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 224, 0, 18); break; 340 case 15: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 264, 0, 18); break; 341 case 16: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 96, 0, 18); break; 342 case 17: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 120, 0, 18); break; 343 case 18: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 144, 0, 18); break; 344 case 19: *srds_div = SRDS_INTERNAL_COMPOSE_PLL_DIV( 198, 0, 18); break; 345 default: 346 EFUN_PRINTF(("ERROR: blackhawk_tsc_INTERNAL_read_pll_div() found invalid pll_mode value: %d\n", pll_mode)); 347 return (blackhawk_tsc_error(sa__, ERR_CODE_PLL_DIV_INVALID)); 348 } 349 } 350 return (ERR_CODE_NONE); 351 }