/***********************license start*************** * Copyright (c) 2011 Cavium Inc. (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Cavium Inc. nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * This Software, including technical data, may be subject to U.S. export control * laws, including the U.S. Export Administration Act and its associated * regulations, and may be subject to export or import regulations in other * countries. * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. ***********************license end**************************************/ /** * @file * * Helper utilities for qlm. * *
$Revision: 70129 $
*/ #ifdef CVMX_BUILD_FOR_LINUX_KERNEL #include #include #include #include #include #include #include #include #else #include "executive-config.h" #include "cvmx-config.h" #include "cvmx.h" #include "cvmx-bootmem.h" #include "cvmx-helper-jtag.h" #include "cvmx-qlm.h" #endif /** * The JTAG chain for CN52XX and CN56XX is 4 * 268 bits long, or 1072. * CN5XXX full chain shift is: * new data => lane 3 => lane 2 => lane 1 => lane 0 => data out * The JTAG chain for CN63XX is 4 * 300 bits long, or 1200. * The JTAG chain for CN68XX is 4 * 304 bits long, or 1216. * The JTAG chain for CN66XX/CN61XX/CNF71XX is 4 * 304 bits long, or 1216. * CN6XXX full chain shift is: * new data => lane 0 => lane 1 => lane 2 => lane 3 => data out * Shift LSB first, get LSB out */ extern const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn52xx[]; extern const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn56xx[]; extern const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn63xx[]; extern const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn66xx[]; extern const __cvmx_qlm_jtag_field_t __cvmx_qlm_jtag_field_cn68xx[]; #define CVMX_QLM_JTAG_UINT32 40 #ifdef CVMX_BUILD_FOR_LINUX_HOST extern void octeon_remote_read_mem(void *buffer, uint64_t physical_address, int length); extern void octeon_remote_write_mem(uint64_t physical_address, const void *buffer, int length); uint32_t __cvmx_qlm_jtag_xor_ref[5][CVMX_QLM_JTAG_UINT32]; #else typedef uint32_t qlm_jtag_uint32_t[CVMX_QLM_JTAG_UINT32]; CVMX_SHARED qlm_jtag_uint32_t *__cvmx_qlm_jtag_xor_ref; #endif /** * Return the number of QLMs supported by the chip * * @return Number of QLMs */ int cvmx_qlm_get_num(void) { if (OCTEON_IS_MODEL(OCTEON_CN68XX)) return 5; else if (OCTEON_IS_MODEL(OCTEON_CN66XX)) return 3; else if (OCTEON_IS_MODEL(OCTEON_CN63XX)) return 3; else if (OCTEON_IS_MODEL(OCTEON_CN61XX)) return 3; else if (OCTEON_IS_MODEL(OCTEON_CN56XX)) return 4; else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CNF71XX)) return 2; //cvmx_dprintf("Warning: cvmx_qlm_get_num: This chip does not have QLMs\n"); return 0; } /** * Return the qlm number based on the interface * * @param interface Interface to look up */ int cvmx_qlm_interface(int interface) { if (OCTEON_IS_MODEL(OCTEON_CN61XX)) { return (interface == 0) ? 2 : 0; } else if (OCTEON_IS_MODEL(OCTEON_CN63XX) || OCTEON_IS_MODEL(OCTEON_CN66XX)) { return 2 - interface; } else { /* Must be cn68XX */ switch(interface) { case 1: return 0; default: return interface; } } } /** * Return number of lanes for a given qlm * * @return Number of lanes */ int cvmx_qlm_get_lanes(int qlm) { if (OCTEON_IS_MODEL(OCTEON_CN61XX) && qlm == 1) return 2; else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) return 2; return 4; } /** * Get the QLM JTAG fields based on Octeon model on the supported chips. * * @return qlm_jtag_field_t structure */ const __cvmx_qlm_jtag_field_t *cvmx_qlm_jtag_get_field(void) { /* Figure out which JTAG chain description we're using */ if (OCTEON_IS_MODEL(OCTEON_CN68XX)) return __cvmx_qlm_jtag_field_cn68xx; else if (OCTEON_IS_MODEL(OCTEON_CN66XX) || OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CNF71XX)) return __cvmx_qlm_jtag_field_cn66xx; else if (OCTEON_IS_MODEL(OCTEON_CN63XX)) return __cvmx_qlm_jtag_field_cn63xx; else if (OCTEON_IS_MODEL(OCTEON_CN56XX)) return __cvmx_qlm_jtag_field_cn56xx; else if (OCTEON_IS_MODEL(OCTEON_CN52XX)) return __cvmx_qlm_jtag_field_cn52xx; else { //cvmx_dprintf("cvmx_qlm_jtag_get_field: Needs update for this chip\n"); return NULL; } } /** * Get the QLM JTAG length by going through qlm_jtag_field for each * Octeon model that is supported * * @return return the length. */ int cvmx_qlm_jtag_get_length(void) { const __cvmx_qlm_jtag_field_t *qlm_ptr = cvmx_qlm_jtag_get_field(); int length = 0; /* Figure out how many bits are in the JTAG chain */ while (qlm_ptr != NULL && qlm_ptr->name) { if (qlm_ptr->stop_bit > length) length = qlm_ptr->stop_bit + 1; qlm_ptr++; } return length; } /** * Initialize the QLM layer */ void cvmx_qlm_init(void) { int qlm; int qlm_jtag_length; char *qlm_jtag_name = "cvmx_qlm_jtag"; int qlm_jtag_size = CVMX_QLM_JTAG_UINT32 * 8 * 4; static uint64_t qlm_base = 0; const cvmx_bootmem_named_block_desc_t *desc; #ifndef CVMX_BUILD_FOR_LINUX_HOST /* Skip actual JTAG accesses on simulator */ if (cvmx_sysinfo_get()->board_type == CVMX_BOARD_TYPE_SIM) return; #endif qlm_jtag_length = cvmx_qlm_jtag_get_length(); if (4 * qlm_jtag_length > (int)sizeof(__cvmx_qlm_jtag_xor_ref[0]) * 8) { cvmx_dprintf("ERROR: cvmx_qlm_init: JTAG chain larger than XOR ref size\n"); return; } /* No need to initialize the initial JTAG state if cvmx_qlm_jtag named block is already created. */ if ((desc = cvmx_bootmem_find_named_block(qlm_jtag_name)) != NULL) { #ifdef CVMX_BUILD_FOR_LINUX_HOST char buffer[qlm_jtag_size]; octeon_remote_read_mem(buffer, desc->base_addr, qlm_jtag_size); memcpy(__cvmx_qlm_jtag_xor_ref, buffer, qlm_jtag_size); #else __cvmx_qlm_jtag_xor_ref = cvmx_phys_to_ptr(desc->base_addr); #endif /* Initialize the internal JTAG */ cvmx_helper_qlm_jtag_init(); return; } /* Create named block to store the initial JTAG state. */ qlm_base = cvmx_bootmem_phy_named_block_alloc(qlm_jtag_size, 0, 0, 128, qlm_jtag_name, CVMX_BOOTMEM_FLAG_END_ALLOC); if (qlm_base == -1ull) { cvmx_dprintf("ERROR: cvmx_qlm_init: Error in creating %s named block\n", qlm_jtag_name); return; } #ifndef CVMX_BUILD_FOR_LINUX_HOST __cvmx_qlm_jtag_xor_ref = cvmx_phys_to_ptr(qlm_base); #endif memset(__cvmx_qlm_jtag_xor_ref, 0, qlm_jtag_size); /* Initialize the internal JTAG */ cvmx_helper_qlm_jtag_init(); /* Read the XOR defaults for the JTAG chain */ for (qlm=0; qlmname) { if (strcmp(name, ptr->name) == 0) return ptr; ptr++; } cvmx_dprintf("__cvmx_qlm_lookup_field: Illegal field name %s\n", name); return NULL; } /** * Get a field in a QLM JTAG chain * * @param qlm QLM to get * @param lane Lane in QLM to get * @param name String name of field * * @return JTAG field value */ uint64_t cvmx_qlm_jtag_get(int qlm, int lane, const char *name) { const __cvmx_qlm_jtag_field_t *field = __cvmx_qlm_lookup_field(name); int qlm_jtag_length = cvmx_qlm_jtag_get_length(); int num_lanes = cvmx_qlm_get_lanes(qlm); if (!field) return 0; /* Capture the current settings */ cvmx_helper_qlm_jtag_capture(qlm); /* Shift past lanes we don't care about. CN6XXX shifts lane 3 first */ cvmx_helper_qlm_jtag_shift_zeros(qlm, qlm_jtag_length * (num_lanes-1-lane)); /* Shift to the start of the field */ cvmx_helper_qlm_jtag_shift_zeros(qlm, field->start_bit); /* Shift out the value and return it */ return cvmx_helper_qlm_jtag_shift(qlm, field->stop_bit - field->start_bit + 1, 0); } /** * Set a field in a QLM JTAG chain * * @param qlm QLM to set * @param lane Lane in QLM to set, or -1 for all lanes * @param name String name of field * @param value Value of the field */ void cvmx_qlm_jtag_set(int qlm, int lane, const char *name, uint64_t value) { int i, l; uint32_t shift_values[CVMX_QLM_JTAG_UINT32]; int num_lanes = cvmx_qlm_get_lanes(qlm); const __cvmx_qlm_jtag_field_t *field = __cvmx_qlm_lookup_field(name); int qlm_jtag_length = cvmx_qlm_jtag_get_length(); int total_length = qlm_jtag_length * num_lanes; int bits = 0; if (!field) return; /* Get the current state */ cvmx_helper_qlm_jtag_capture(qlm); for (i=0; istart_bit + (num_lanes-1-l)*qlm_jtag_length; bits <= field->stop_bit + (num_lanes-1-l)*qlm_jtag_length; bits++) { if (new_value & 1) shift_values[bits/32] |= 1<<(bits&31); else shift_values[bits/32] &= ~(1<<(bits&31)); new_value>>=1; } } /* Shift out data and xor with reference */ while (bits < total_length) { uint32_t shift = shift_values[bits/32] ^ __cvmx_qlm_jtag_xor_ref[qlm][bits/32]; int width = total_length - bits; if (width > 32) width = 32; cvmx_helper_qlm_jtag_shift(qlm, width, shift); bits += 32; } /* Update the new data */ cvmx_helper_qlm_jtag_update(qlm); /* Always give the QLM 1ms to settle after every update. This may not always be needed, but some of the options make significant electrical changes */ cvmx_wait_usec(1000); } /** * Errata G-16094: QLM Gen2 Equalizer Default Setting Change. * CN68XX pass 1.x and CN66XX pass 1.x QLM tweak. This function tweaks the * JTAG setting for a QLMs to run better at 5 and 6.25Ghz. */ void __cvmx_qlm_speed_tweak(void) { cvmx_mio_qlmx_cfg_t qlm_cfg; int num_qlms = 0; int qlm; if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X)) num_qlms = 5; else if (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X)) num_qlms = 3; else return; /* Loop through the QLMs */ for (qlm = 0; qlm < num_qlms; qlm++) { /* Read the QLM speed */ qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(qlm)); /* If the QLM is at 6.25Ghz or 5Ghz then program JTAG */ if ((qlm_cfg.s.qlm_spd == 5) || (qlm_cfg.s.qlm_spd == 12) || (qlm_cfg.s.qlm_spd == 0) || (qlm_cfg.s.qlm_spd == 6) || (qlm_cfg.s.qlm_spd == 11)) { cvmx_qlm_jtag_set(qlm, -1, "rx_cap_gen2", 0x1); cvmx_qlm_jtag_set(qlm, -1, "rx_eq_gen2", 0x8); } } } /** * Errata G-16174: QLM Gen2 PCIe IDLE DAC change. * CN68XX pass 1.x, CN66XX pass 1.x and CN63XX pass 1.0-2.2 QLM tweak. * This function tweaks the JTAG setting for a QLMs for PCIe to run better. */ void __cvmx_qlm_pcie_idle_dac_tweak(void) { int num_qlms = 0; int qlm; if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X)) num_qlms = 5; else if (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X)) num_qlms = 3; else if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X) || OCTEON_IS_MODEL(OCTEON_CN63XX_PASS2_X)) num_qlms = 3; else return; /* Loop through the QLMs */ for (qlm = 0; qlm < num_qlms; qlm++) cvmx_qlm_jtag_set(qlm, -1, "idle_dac", 0x2); } #ifndef CVMX_BUILD_FOR_LINUX_HOST /** * Get the speed (Gbaud) of the QLM in Mhz. * * @param qlm QLM to examine * * @return Speed in Mhz */ int cvmx_qlm_get_gbaud_mhz(int qlm) { if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { if (qlm == 2) { cvmx_gmxx_inf_mode_t inf_mode; inf_mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(0)); switch (inf_mode.s.speed) { case 0: return 5000; /* 5 Gbaud */ case 1: return 2500; /* 2.5 Gbaud */ case 2: return 2500; /* 2.5 Gbaud */ case 3: return 1250; /* 1.25 Gbaud */ case 4: return 1250; /* 1.25 Gbaud */ case 5: return 6250; /* 6.25 Gbaud */ case 6: return 5000; /* 5 Gbaud */ case 7: return 2500; /* 2.5 Gbaud */ case 8: return 3125; /* 3.125 Gbaud */ case 9: return 2500; /* 2.5 Gbaud */ case 10: return 1250; /* 1.25 Gbaud */ case 11: return 5000; /* 5 Gbaud */ case 12: return 6250; /* 6.25 Gbaud */ case 13: return 3750; /* 3.75 Gbaud */ case 14: return 3125; /* 3.125 Gbaud */ default: return 0; /* Disabled */ } } else { cvmx_sriox_status_reg_t status_reg; status_reg.u64 = cvmx_read_csr(CVMX_SRIOX_STATUS_REG(qlm)); if (status_reg.s.srio) { cvmx_sriomaintx_port_0_ctl2_t sriomaintx_port_0_ctl2; sriomaintx_port_0_ctl2.u32 = cvmx_read_csr(CVMX_SRIOMAINTX_PORT_0_CTL2(qlm)); switch (sriomaintx_port_0_ctl2.s.sel_baud) { case 1: return 1250; /* 1.25 Gbaud */ case 2: return 2500; /* 2.5 Gbaud */ case 3: return 3125; /* 3.125 Gbaud */ case 4: return 5000; /* 5 Gbaud */ case 5: return 6250; /* 6.250 Gbaud */ default: return 0; /* Disabled */ } } else { cvmx_pciercx_cfg032_t pciercx_cfg032; pciercx_cfg032.u32 = cvmx_read_csr(CVMX_PCIERCX_CFG032(qlm)); switch (pciercx_cfg032.s.ls) { case 1: return 2500; case 2: return 5000; case 4: return 8000; default: { cvmx_mio_rst_boot_t mio_rst_boot; mio_rst_boot.u64 = cvmx_read_csr(CVMX_MIO_RST_BOOT); if ((qlm == 0) && mio_rst_boot.s.qlm0_spd == 0xf) return 0; if ((qlm == 1) && mio_rst_boot.s.qlm1_spd == 0xf) return 0; return 5000; /* Best guess I can make */ } } } } } else if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF71XX)) { cvmx_mio_qlmx_cfg_t qlm_cfg; qlm_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(qlm)); switch (qlm_cfg.s.qlm_spd) { case 0: return 5000; /* 5 Gbaud */ case 1: return 2500; /* 2.5 Gbaud */ case 2: return 2500; /* 2.5 Gbaud */ case 3: return 1250; /* 1.25 Gbaud */ case 4: return 1250; /* 1.25 Gbaud */ case 5: return 6250; /* 6.25 Gbaud */ case 6: return 5000; /* 5 Gbaud */ case 7: return 2500; /* 2.5 Gbaud */ case 8: return 3125; /* 3.125 Gbaud */ case 9: return 2500; /* 2.5 Gbaud */ case 10: return 1250; /* 1.25 Gbaud */ case 11: return 5000; /* 5 Gbaud */ case 12: return 6250; /* 6.25 Gbaud */ case 13: return 3750; /* 3.75 Gbaud */ case 14: return 3125; /* 3.125 Gbaud */ default: return 0; /* Disabled */ } } return 0; } #endif /* * Read QLM and return status based on CN66XX. * @return Return 1 if QLM is SGMII * 2 if QLM is XAUI * 3 if QLM is PCIe gen2 / gen1 * 4 if QLM is SRIO 1x4 short / long * 5 if QLM is SRIO 2x2 short / long * 6 if QLM is SRIO 4x1 short / long * 7 if QLM is PCIe 1x2 gen2 / gen1 * 8 if QLM is PCIe 2x1 gen2 / gen1 * 9 if QLM is ILK * 10 if QLM is RXAUI * -1 otherwise */ int cvmx_qlm_get_status(int qlm) { cvmx_mio_qlmx_cfg_t qlmx_cfg; if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { qlmx_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(qlm)); /* QLM is disabled when QLM SPD is 15. */ if (qlmx_cfg.s.qlm_spd == 15) return -1; switch (qlmx_cfg.s.qlm_cfg) { case 0: /* PCIE */ return 3; case 1: /* ILK */ return 9; case 2: /* SGMII */ return 1; case 3: /* XAUI */ return 2; case 7: /* RXAUI */ return 10; default: return -1; } } else if (OCTEON_IS_MODEL(OCTEON_CN66XX)) { qlmx_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(qlm)); /* QLM is disabled when QLM SPD is 15. */ if (qlmx_cfg.s.qlm_spd == 15) return -1; switch (qlmx_cfg.s.qlm_cfg) { case 0x9: /* SGMII */ return 1; case 0xb: /* XAUI */ return 2; case 0x0: /* PCIE gen2 */ case 0x8: /* PCIE gen2 (alias) */ case 0x2: /* PCIE gen1 */ case 0xa: /* PCIE gen1 (alias) */ return 3; case 0x1: /* SRIO 1x4 short */ case 0x3: /* SRIO 1x4 long */ return 4; case 0x4: /* SRIO 2x2 short */ case 0x6: /* SRIO 2x2 long */ return 5; case 0x5: /* SRIO 4x1 short */ case 0x7: /* SRIO 4x1 long */ if (!OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0)) return 6; default: return -1; } } else if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { cvmx_sriox_status_reg_t status_reg; /* For now skip qlm2 */ if (qlm == 2) { cvmx_gmxx_inf_mode_t inf_mode; inf_mode.u64 = cvmx_read_csr(CVMX_GMXX_INF_MODE(0)); if (inf_mode.s.speed == 15) return -1; else if(inf_mode.s.mode == 0) return 1; else return 2; } status_reg.u64 = cvmx_read_csr(CVMX_SRIOX_STATUS_REG(qlm)); if (status_reg.s.srio) return 4; else return 3; } else if (OCTEON_IS_MODEL(OCTEON_CN61XX)) { qlmx_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(qlm)); /* QLM is disabled when QLM SPD is 15. */ if (qlmx_cfg.s.qlm_spd == 15) return -1; switch(qlm) { case 0: switch (qlmx_cfg.s.qlm_cfg) { case 0: /* PCIe 1x4 gen2 / gen1 */ return 3; case 2: /* SGMII */ return 1; case 3: /* XAUI */ return 2; default: return -1; } break; case 1: switch (qlmx_cfg.s.qlm_cfg) { case 0: /* PCIe 1x2 gen2 / gen1 */ return 7; case 1: /* PCIe 2x1 gen2 / gen1 */ return 8; default: return -1; } break; case 2: switch (qlmx_cfg.s.qlm_cfg) { case 2: /* SGMII */ return 1; case 3: /* XAUI */ return 2; default: return -1; } break; } } else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) { qlmx_cfg.u64 = cvmx_read_csr(CVMX_MIO_QLMX_CFG(qlm)); /* QLM is disabled when QLM SPD is 15. */ if (qlmx_cfg.s.qlm_spd == 15) return -1; switch(qlm) { case 0: if (qlmx_cfg.s.qlm_cfg == 2) /* SGMII */ return 1; break; case 1: switch (qlmx_cfg.s.qlm_cfg) { case 0: /* PCIe 1x2 gen2 / gen1 */ return 7; case 1: /* PCIe 2x1 gen2 / gen1 */ return 8; default: return -1; } break; } } return -1; }