| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2020 Marvell International Ltd. |
| * |
| * Helper utilities for qlm. |
| */ |
| |
| #include <log.h> |
| #include <time.h> |
| #include <asm/global_data.h> |
| #include <linux/delay.h> |
| |
| #include <mach/cvmx-regs.h> |
| #include <mach/octeon-model.h> |
| #include <mach/cvmx-fuse.h> |
| #include <mach/octeon-feature.h> |
| #include <mach/cvmx-qlm.h> |
| #include <mach/octeon_qlm.h> |
| #include <mach/cvmx-pcie.h> |
| #include <mach/cvmx-helper.h> |
| #include <mach/cvmx-helper-util.h> |
| #include <mach/cvmx-bgxx-defs.h> |
| #include <mach/cvmx-ciu-defs.h> |
| #include <mach/cvmx-gmxx-defs.h> |
| #include <mach/cvmx-gserx-defs.h> |
| #include <mach/cvmx-mio-defs.h> |
| #include <mach/cvmx-pciercx-defs.h> |
| #include <mach/cvmx-pemx-defs.h> |
| #include <mach/cvmx-pexp-defs.h> |
| #include <mach/cvmx-rst-defs.h> |
| #include <mach/cvmx-sata-defs.h> |
| #include <mach/cvmx-sli-defs.h> |
| #include <mach/cvmx-sriomaintx-defs.h> |
| #include <mach/cvmx-sriox-defs.h> |
| |
| #include <mach/cvmx-helper.h> |
| #include <mach/cvmx-helper-jtag.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| /* |
| * Their is a copy of this in bootloader qlm configuration, make sure |
| * to update both the places till i figure out |
| */ |
| #define R_25G_REFCLK100 0x0 |
| #define R_5G_REFCLK100 0x1 |
| #define R_8G_REFCLK100 0x2 |
| #define R_125G_REFCLK15625_KX 0x3 |
| #define R_3125G_REFCLK15625_XAUI 0x4 |
| #define R_103125G_REFCLK15625_KR 0x5 |
| #define R_125G_REFCLK15625_SGMII 0x6 |
| #define R_5G_REFCLK15625_QSGMII 0x7 |
| #define R_625G_REFCLK15625_RXAUI 0x8 |
| #define R_25G_REFCLK125 0x9 |
| #define R_5G_REFCLK125 0xa |
| #define R_8G_REFCLK125 0xb |
| |
| static const int REF_100MHZ = 100000000; |
| static const int REF_125MHZ = 125000000; |
| static const int REF_156MHZ = 156250000; |
| |
| static qlm_jtag_uint32_t *__cvmx_qlm_jtag_xor_ref; |
| |
| /** |
| * 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_CNF71XX)) |
| return 2; |
| else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) |
| return 8; |
| else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) |
| return 7; |
| else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) |
| return 9; |
| return 0; |
| } |
| |
| /** |
| * Return the qlm number based on the interface |
| * |
| * @param xiface interface to look up |
| * |
| * @return the qlm number based on the xiface |
| */ |
| int cvmx_qlm_interface(int xiface) |
| { |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN61XX)) { |
| return (xi.interface == 0) ? 2 : 0; |
| } else if (OCTEON_IS_MODEL(OCTEON_CN63XX) || OCTEON_IS_MODEL(OCTEON_CN66XX)) { |
| return 2 - xi.interface; |
| } else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) { |
| if (xi.interface == 0) |
| return 0; |
| |
| debug("Warning: %s: Invalid interface %d\n", |
| __func__, xi.interface); |
| } else if (octeon_has_feature(OCTEON_FEATURE_BGX)) { |
| debug("Warning: not supported\n"); |
| return -1; |
| } |
| |
| /* Must be cn68XX */ |
| switch (xi.interface) { |
| case 1: |
| return 0; |
| default: |
| return xi.interface; |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Return the qlm number based for a port in the interface |
| * |
| * @param xiface interface to look up |
| * @param index index in an interface |
| * |
| * @return the qlm number based on the xiface |
| */ |
| int cvmx_qlm_lmac(int xiface, int index) |
| { |
| struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { |
| cvmx_bgxx_cmr_global_config_t gconfig; |
| cvmx_gserx_phy_ctl_t phy_ctl; |
| cvmx_gserx_cfg_t gserx_cfg; |
| int qlm; |
| |
| if (xi.interface < 6) { |
| if (xi.interface < 2) { |
| gconfig.u64 = |
| csr_rd_node(xi.node, |
| CVMX_BGXX_CMR_GLOBAL_CONFIG(xi.interface)); |
| if (gconfig.s.pmux_sds_sel) |
| qlm = xi.interface + 2; /* QLM 2 or 3 */ |
| else |
| qlm = xi.interface; /* QLM 0 or 1 */ |
| } else { |
| qlm = xi.interface + 2; /* QLM 4-7 */ |
| } |
| |
| /* make sure the QLM is powered up and out of reset */ |
| phy_ctl.u64 = csr_rd_node(xi.node, CVMX_GSERX_PHY_CTL(qlm)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| gserx_cfg.u64 = csr_rd_node(xi.node, CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.bgx) |
| return qlm; |
| else |
| return -1; |
| } else if (xi.interface <= 7) { /* ILK */ |
| int qlm; |
| |
| for (qlm = 4; qlm < 8; qlm++) { |
| /* Make sure the QLM is powered and out of reset */ |
| phy_ctl.u64 = csr_rd_node(xi.node, CVMX_GSERX_PHY_CTL(qlm)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| continue; |
| /* Make sure the QLM is in ILK mode */ |
| gserx_cfg.u64 = csr_rd_node(xi.node, CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.ila) |
| return qlm; |
| } |
| } |
| return -1; |
| } else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) { |
| cvmx_gserx_phy_ctl_t phy_ctl; |
| cvmx_gserx_cfg_t gserx_cfg; |
| int qlm; |
| |
| /* (interface)0->QLM2, 1->QLM3, 2->DLM5/3->DLM6 */ |
| if (xi.interface < 2) { |
| qlm = xi.interface + 2; /* (0,1)->ret(2,3) */ |
| |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(qlm)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| |
| gserx_cfg.u64 = csr_rd(CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.bgx) |
| return qlm; |
| else |
| return -1; |
| } else if (xi.interface == 2) { |
| cvmx_gserx_cfg_t g1, g2; |
| |
| g1.u64 = csr_rd(CVMX_GSERX_CFG(5)); |
| g2.u64 = csr_rd(CVMX_GSERX_CFG(6)); |
| /* Check if both QLM5 & QLM6 are BGX2 */ |
| if (g2.s.bgx) { |
| if (g1.s.bgx) { |
| cvmx_gserx_phy_ctl_t phy_ctl1; |
| |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(5)); |
| phy_ctl1.u64 = csr_rd(CVMX_GSERX_PHY_CTL(6)); |
| if ((phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) && |
| (phy_ctl1.s.phy_pd || phy_ctl1.s.phy_reset)) |
| return -1; |
| if (index >= 2) |
| return 6; |
| return 5; |
| } else { /* QLM6 is BGX2 */ |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(6)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| return 6; |
| } |
| } else if (g1.s.bgx) { |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(5)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| return 5; |
| } |
| } |
| return -1; |
| } else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) { |
| cvmx_gserx_phy_ctl_t phy_ctl; |
| cvmx_gserx_cfg_t gserx_cfg; |
| int qlm; |
| |
| if (xi.interface == 0) { |
| cvmx_gserx_cfg_t g1, g2; |
| |
| g1.u64 = csr_rd(CVMX_GSERX_CFG(4)); |
| g2.u64 = csr_rd(CVMX_GSERX_CFG(5)); |
| /* Check if both QLM4 & QLM5 are BGX0 */ |
| if (g2.s.bgx) { |
| if (g1.s.bgx) { |
| cvmx_gserx_phy_ctl_t phy_ctl1; |
| |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(4)); |
| phy_ctl1.u64 = csr_rd(CVMX_GSERX_PHY_CTL(5)); |
| if ((phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) && |
| (phy_ctl1.s.phy_pd || phy_ctl1.s.phy_reset)) |
| return -1; |
| if (index >= 2) |
| return 5; |
| return 4; |
| } |
| |
| /* QLM5 is BGX0 */ |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(5)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| return 5; |
| } else if (g1.s.bgx) { |
| phy_ctl.u64 = csr_rd(CVMX_GSERX_PHY_CTL(4)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| return 4; |
| } |
| } else if (xi.interface < 2) { |
| qlm = (xi.interface == 1) ? 2 : 3; |
| gserx_cfg.u64 = csr_rd(CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.srio) |
| return qlm; |
| } |
| return -1; |
| } |
| return -1; |
| } |
| |
| /** |
| * Return if only DLM5/DLM6/DLM5+DLM6 is used by BGX |
| * |
| * @param BGX BGX to search for. |
| * |
| * @return muxes used 0 = DLM5+DLM6, 1 = DLM5, 2 = DLM6. |
| */ |
| int cvmx_qlm_mux_interface(int bgx) |
| { |
| int mux = 0; |
| cvmx_gserx_cfg_t gser1, gser2; |
| int qlm1, qlm2; |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX) && bgx != 2) |
| return -1; |
| else if (OCTEON_IS_MODEL(OCTEON_CNF75XX) && bgx != 0) |
| return -1; |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX)) { |
| qlm1 = 5; |
| qlm2 = 6; |
| } else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) { |
| qlm1 = 4; |
| qlm2 = 5; |
| } else { |
| return -1; |
| } |
| |
| gser1.u64 = csr_rd(CVMX_GSERX_CFG(qlm1)); |
| gser2.u64 = csr_rd(CVMX_GSERX_CFG(qlm2)); |
| |
| if (gser1.s.bgx && gser2.s.bgx) |
| mux = 0; |
| else if (gser1.s.bgx) |
| mux = 1; // BGX2 is using DLM5 only |
| else if (gser2.s.bgx) |
| mux = 2; // BGX2 is using DLM6 only |
| |
| return mux; |
| } |
| |
| /** |
| * Return number of lanes for a given qlm |
| * |
| * @param qlm QLM to examine |
| * |
| * @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; |
| else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) |
| return (qlm < 4) ? 4 /*QLM0,1,2,3*/ : 2 /*DLM4,5,6*/; |
| else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) |
| return (qlm == 2 || qlm == 3) ? 4 /*QLM2,3*/ : 2 /*DLM0,1,4,5*/; |
| 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; |
| } |
| |
| 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 && 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) |
| { |
| if (OCTEON_IS_OCTEON3()) |
| return; |
| |
| /* ToDo: No support for non-Octeon 3 yet */ |
| printf("Please add support for unsupported Octeon SoC\n"); |
| } |
| |
| /** |
| * Lookup the bit information for a JTAG field name |
| * |
| * @param name Name to lookup |
| * |
| * @return Field info, or NULL on failure |
| */ |
| static const __cvmx_qlm_jtag_field_t *__cvmx_qlm_lookup_field(const char *name) |
| { |
| const __cvmx_qlm_jtag_field_t *ptr = cvmx_qlm_jtag_get_field(); |
| |
| while (ptr->name) { |
| if (strcmp(name, ptr->name) == 0) |
| return ptr; |
| ptr++; |
| } |
| |
| debug("%s: Illegal field name %s\n", __func__, 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/7XXX shifts lane 0 first, |
| * CN3XXX/5XXX shifts lane 3 first |
| */ |
| /* Shift to the start of the field */ |
| cvmx_helper_qlm_jtag_shift_zeros(qlm, |
| qlm_jtag_length * (num_lanes - 1 - lane)); |
| 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; |
| u32 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; i < CVMX_QLM_JTAG_UINT32; i++) |
| shift_values[i] = cvmx_helper_qlm_jtag_shift(qlm, 32, 0); |
| |
| /* Put new data in our local array */ |
| for (l = 0; l < num_lanes; l++) { |
| u64 new_value = value; |
| int bits; |
| int adj_lanes; |
| |
| if (l != lane && lane != -1) |
| continue; |
| |
| adj_lanes = (num_lanes - 1 - l) * qlm_jtag_length; |
| |
| for (bits = field->start_bit + adj_lanes; bits <= field->stop_bit + adj_lanes; |
| 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) { |
| u32 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 |
| */ |
| udelay(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 = cvmx_qlm_get_num(); |
| int qlm; |
| |
| /* Workaround for Errata (G-16467) */ |
| if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X)) { |
| for (qlm = 0; qlm < num_qlms; qlm++) { |
| int ir50dac; |
| |
| /* |
| * This workaround only applies to QLMs running at |
| * 6.25Ghz |
| */ |
| if (cvmx_qlm_get_gbaud_mhz(qlm) == 6250) { |
| #ifdef CVMX_QLM_DUMP_STATE |
| debug("%s:%d: QLM%d: Applying workaround for Errata G-16467\n", |
| __func__, __LINE__, qlm); |
| cvmx_qlm_display_registers(qlm); |
| debug("\n"); |
| #endif |
| cvmx_qlm_jtag_set(qlm, -1, "cfg_cdr_trunc", 0); |
| /* Hold the QLM in reset */ |
| cvmx_qlm_jtag_set(qlm, -1, "cfg_rst_n_set", 0); |
| cvmx_qlm_jtag_set(qlm, -1, "cfg_rst_n_clr", 1); |
| /* Forcfe TX to be idle */ |
| cvmx_qlm_jtag_set(qlm, -1, "cfg_tx_idle_clr", 0); |
| cvmx_qlm_jtag_set(qlm, -1, "cfg_tx_idle_set", 1); |
| if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_0)) { |
| ir50dac = cvmx_qlm_jtag_get(qlm, 0, "ir50dac"); |
| while (++ir50dac <= 31) |
| cvmx_qlm_jtag_set(qlm, -1, "ir50dac", ir50dac); |
| } |
| cvmx_qlm_jtag_set(qlm, -1, "div4_byp", 0); |
| cvmx_qlm_jtag_set(qlm, -1, "clkf_byp", 16); |
| cvmx_qlm_jtag_set(qlm, -1, "serdes_pll_byp", 1); |
| cvmx_qlm_jtag_set(qlm, -1, "spdsel_byp", 1); |
| #ifdef CVMX_QLM_DUMP_STATE |
| debug("%s:%d: QLM%d: Done applying workaround for Errata G-16467\n", |
| __func__, __LINE__, qlm); |
| cvmx_qlm_display_registers(qlm); |
| debug("\n\n"); |
| #endif |
| /* |
| * The QLM will be taken out of reset later |
| * when ILK/XAUI are initialized. |
| */ |
| } |
| } |
| } else if (OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X) || |
| OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_X)) { |
| /* Loop through the QLMs */ |
| for (qlm = 0; qlm < num_qlms; qlm++) { |
| /* Read the QLM speed */ |
| qlm_cfg.u64 = csr_rd(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)) |
| 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); |
| } |
| |
| void __cvmx_qlm_pcie_cfg_rxd_set_tweak(int qlm, int lane) |
| { |
| if (OCTEON_IS_MODEL(OCTEON_CN6XXX) || OCTEON_IS_MODEL(OCTEON_CNF71XX)) |
| cvmx_qlm_jtag_set(qlm, lane, "cfg_rxd_set", 0x1); |
| } |
| |
| /** |
| * Get the speed (Gbaud) of the QLM in Mhz for a given node. |
| * |
| * @param node node of the QLM |
| * @param qlm QLM to examine |
| * |
| * @return Speed in Mhz |
| */ |
| int cvmx_qlm_get_gbaud_mhz_node(int node, int qlm) |
| { |
| cvmx_gserx_lane_mode_t lane_mode; |
| cvmx_gserx_cfg_t cfg; |
| |
| if (!octeon_has_feature(OCTEON_FEATURE_MULTINODE)) |
| return 0; |
| |
| if (qlm >= 8) |
| return -1; /* FIXME for OCI */ |
| /* Check if QLM is configured */ |
| cfg.u64 = csr_rd_node(node, CVMX_GSERX_CFG(qlm)); |
| if (cfg.u64 == 0) |
| return -1; |
| if (cfg.s.pcie) { |
| int pem = 0; |
| cvmx_pemx_cfg_t pemx_cfg; |
| |
| switch (qlm) { |
| case 0: /* Either PEM0 x4 of PEM0 x8 */ |
| pem = 0; |
| break; |
| case 1: /* Either PEM0 x4 of PEM1 x4 */ |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(0)); |
| if (pemx_cfg.cn78xx.lanes8) |
| pem = 0; |
| else |
| pem = 1; |
| break; |
| case 2: /* Either PEM2 x4 of PEM2 x8 */ |
| pem = 2; |
| break; |
| case 3: /* Either PEM2 x8 of PEM3 x4 or x8 */ |
| /* Can be last 4 lanes of PEM2 */ |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(2)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| pem = 2; |
| } else { |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(3)); |
| if (pemx_cfg.cn78xx.lanes8) |
| pem = 3; |
| else |
| pem = 2; |
| } |
| break; |
| case 4: /* Either PEM3 x8 of PEM3 x4 */ |
| pem = 3; |
| break; |
| default: |
| debug("QLM%d: Should be in PCIe mode\n", qlm); |
| break; |
| } |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(pem)); |
| switch (pemx_cfg.s.md) { |
| case 0: /* Gen1 */ |
| return 2500; |
| case 1: /* Gen2 */ |
| return 5000; |
| case 2: /* Gen3 */ |
| return 8000; |
| default: |
| return 0; |
| } |
| } else { |
| lane_mode.u64 = csr_rd_node(node, CVMX_GSERX_LANE_MODE(qlm)); |
| switch (lane_mode.s.lmode) { |
| case R_25G_REFCLK100: |
| return 2500; |
| case R_5G_REFCLK100: |
| return 5000; |
| case R_8G_REFCLK100: |
| return 8000; |
| case R_125G_REFCLK15625_KX: |
| return 1250; |
| case R_3125G_REFCLK15625_XAUI: |
| return 3125; |
| case R_103125G_REFCLK15625_KR: |
| return 10312; |
| case R_125G_REFCLK15625_SGMII: |
| return 1250; |
| case R_5G_REFCLK15625_QSGMII: |
| return 5000; |
| case R_625G_REFCLK15625_RXAUI: |
| return 6250; |
| case R_25G_REFCLK125: |
| return 2500; |
| case R_5G_REFCLK125: |
| return 5000; |
| case R_8G_REFCLK125: |
| return 8000; |
| default: |
| return 0; |
| } |
| } |
| } |
| |
| /** |
| * 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 = csr_rd(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 = csr_rd(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 = |
| csr_rd(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 = csr_rd(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 = csr_rd(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; |
| |
| /* Best guess I can make */ |
| return 5000; |
| } |
| } |
| } |
| } |
| } else if (OCTEON_IS_OCTEON2()) { |
| cvmx_mio_qlmx_cfg_t qlm_cfg; |
| |
| qlm_cfg.u64 = csr_rd(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 */ |
| } |
| } else if (OCTEON_IS_MODEL(OCTEON_CN70XX)) { |
| cvmx_gserx_dlmx_mpll_multiplier_t mpll_multiplier; |
| u64 meas_refclock; |
| u64 freq; |
| |
| /* Measure the reference clock */ |
| meas_refclock = cvmx_qlm_measure_clock(qlm); |
| /* Multiply to get the final frequency */ |
| mpll_multiplier.u64 = csr_rd(CVMX_GSERX_DLMX_MPLL_MULTIPLIER(qlm, 0)); |
| freq = meas_refclock * mpll_multiplier.s.mpll_multiplier; |
| freq = (freq + 500000) / 1000000; |
| |
| return freq; |
| } else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { |
| return cvmx_qlm_get_gbaud_mhz_node(cvmx_get_node_num(), qlm); |
| } else if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX)) { |
| cvmx_gserx_lane_mode_t lane_mode; |
| |
| lane_mode.u64 = csr_rd(CVMX_GSERX_LANE_MODE(qlm)); |
| switch (lane_mode.s.lmode) { |
| case R_25G_REFCLK100: |
| return 2500; |
| case R_5G_REFCLK100: |
| return 5000; |
| case R_8G_REFCLK100: |
| return 8000; |
| case R_125G_REFCLK15625_KX: |
| return 1250; |
| case R_3125G_REFCLK15625_XAUI: |
| return 3125; |
| case R_103125G_REFCLK15625_KR: |
| return 10312; |
| case R_125G_REFCLK15625_SGMII: |
| return 1250; |
| case R_5G_REFCLK15625_QSGMII: |
| return 5000; |
| case R_625G_REFCLK15625_RXAUI: |
| return 6250; |
| case R_25G_REFCLK125: |
| return 2500; |
| case R_5G_REFCLK125: |
| return 5000; |
| case R_8G_REFCLK125: |
| return 8000; |
| default: |
| return 0; |
| } |
| } |
| return 0; |
| } |
| |
| static enum cvmx_qlm_mode __cvmx_qlm_get_mode_cn70xx(int qlm) |
| { |
| switch (qlm) { |
| case 0: /* DLM0/DLM1 - SGMII/QSGMII/RXAUI */ |
| { |
| union cvmx_gmxx_inf_mode inf_mode0, inf_mode1; |
| |
| inf_mode0.u64 = csr_rd(CVMX_GMXX_INF_MODE(0)); |
| inf_mode1.u64 = csr_rd(CVMX_GMXX_INF_MODE(1)); |
| |
| /* SGMII0 SGMII1 */ |
| switch (inf_mode0.s.mode) { |
| case CVMX_GMX_INF_MODE_SGMII: |
| switch (inf_mode1.s.mode) { |
| case CVMX_GMX_INF_MODE_SGMII: |
| return CVMX_QLM_MODE_SGMII_SGMII; |
| case CVMX_GMX_INF_MODE_QSGMII: |
| return CVMX_QLM_MODE_SGMII_QSGMII; |
| default: |
| return CVMX_QLM_MODE_SGMII_DISABLED; |
| } |
| case CVMX_GMX_INF_MODE_QSGMII: |
| switch (inf_mode1.s.mode) { |
| case CVMX_GMX_INF_MODE_SGMII: |
| return CVMX_QLM_MODE_QSGMII_SGMII; |
| case CVMX_GMX_INF_MODE_QSGMII: |
| return CVMX_QLM_MODE_QSGMII_QSGMII; |
| default: |
| return CVMX_QLM_MODE_QSGMII_DISABLED; |
| } |
| case CVMX_GMX_INF_MODE_RXAUI: |
| return CVMX_QLM_MODE_RXAUI_1X2; |
| default: |
| switch (inf_mode1.s.mode) { |
| case CVMX_GMX_INF_MODE_SGMII: |
| return CVMX_QLM_MODE_DISABLED_SGMII; |
| case CVMX_GMX_INF_MODE_QSGMII: |
| return CVMX_QLM_MODE_DISABLED_QSGMII; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| } |
| } |
| case 1: /* Sata / pem0 */ |
| { |
| union cvmx_gserx_sata_cfg sata_cfg; |
| union cvmx_pemx_cfg pem0_cfg; |
| |
| sata_cfg.u64 = csr_rd(CVMX_GSERX_SATA_CFG(0)); |
| pem0_cfg.u64 = csr_rd(CVMX_PEMX_CFG(0)); |
| |
| switch (pem0_cfg.cn70xx.md) { |
| case CVMX_PEM_MD_GEN2_2LANE: |
| case CVMX_PEM_MD_GEN1_2LANE: |
| return CVMX_QLM_MODE_PCIE_1X2; |
| case CVMX_PEM_MD_GEN2_1LANE: |
| case CVMX_PEM_MD_GEN1_1LANE: |
| if (sata_cfg.s.sata_en) |
| /* Both PEM0 and PEM1 */ |
| return CVMX_QLM_MODE_PCIE_2X1; |
| |
| /* Only PEM0 */ |
| return CVMX_QLM_MODE_PCIE_1X1; |
| case CVMX_PEM_MD_GEN2_4LANE: |
| case CVMX_PEM_MD_GEN1_4LANE: |
| return CVMX_QLM_MODE_PCIE; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| } |
| case 2: { |
| union cvmx_gserx_sata_cfg sata_cfg; |
| union cvmx_pemx_cfg pem0_cfg, pem1_cfg, pem2_cfg; |
| |
| sata_cfg.u64 = csr_rd(CVMX_GSERX_SATA_CFG(0)); |
| pem0_cfg.u64 = csr_rd(CVMX_PEMX_CFG(0)); |
| pem1_cfg.u64 = csr_rd(CVMX_PEMX_CFG(1)); |
| pem2_cfg.u64 = csr_rd(CVMX_PEMX_CFG(2)); |
| |
| if (sata_cfg.s.sata_en) |
| return CVMX_QLM_MODE_SATA_2X1; |
| if (pem0_cfg.cn70xx.md == CVMX_PEM_MD_GEN2_4LANE || |
| pem0_cfg.cn70xx.md == CVMX_PEM_MD_GEN1_4LANE) |
| return CVMX_QLM_MODE_PCIE; |
| if (pem1_cfg.cn70xx.md == CVMX_PEM_MD_GEN2_2LANE || |
| pem1_cfg.cn70xx.md == CVMX_PEM_MD_GEN1_2LANE) { |
| return CVMX_QLM_MODE_PCIE_1X2; |
| } |
| if (pem1_cfg.cn70xx.md == CVMX_PEM_MD_GEN2_1LANE || |
| pem1_cfg.cn70xx.md == CVMX_PEM_MD_GEN1_1LANE) { |
| if (pem2_cfg.cn70xx.md == CVMX_PEM_MD_GEN2_1LANE || |
| pem2_cfg.cn70xx.md == CVMX_PEM_MD_GEN1_1LANE) { |
| return CVMX_QLM_MODE_PCIE_2X1; |
| } else { |
| return CVMX_QLM_MODE_PCIE_1X1; |
| } |
| } |
| if (pem2_cfg.cn70xx.md == CVMX_PEM_MD_GEN2_1LANE || |
| pem2_cfg.cn70xx.md == CVMX_PEM_MD_GEN1_1LANE) |
| return CVMX_QLM_MODE_PCIE_2X1; |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| |
| /* |
| * Get the DLM mode for the interface based on the interface type. |
| * |
| * @param interface_type 0 - SGMII/QSGMII/RXAUI interface |
| * 1 - PCIe |
| * 2 - SATA |
| * @param interface interface to use |
| * @return the qlm mode the interface is |
| */ |
| enum cvmx_qlm_mode cvmx_qlm_get_dlm_mode(int interface_type, int interface) |
| { |
| switch (interface_type) { |
| case 0: /* SGMII/QSGMII/RXAUI */ |
| { |
| enum cvmx_qlm_mode qlm_mode = __cvmx_qlm_get_mode_cn70xx(0); |
| |
| switch (interface) { |
| case 0: |
| switch (qlm_mode) { |
| case CVMX_QLM_MODE_SGMII_SGMII: |
| case CVMX_QLM_MODE_SGMII_DISABLED: |
| case CVMX_QLM_MODE_SGMII_QSGMII: |
| return CVMX_QLM_MODE_SGMII; |
| case CVMX_QLM_MODE_QSGMII_QSGMII: |
| case CVMX_QLM_MODE_QSGMII_DISABLED: |
| case CVMX_QLM_MODE_QSGMII_SGMII: |
| return CVMX_QLM_MODE_QSGMII; |
| case CVMX_QLM_MODE_RXAUI_1X2: |
| return CVMX_QLM_MODE_RXAUI; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| case 1: |
| switch (qlm_mode) { |
| case CVMX_QLM_MODE_SGMII_SGMII: |
| case CVMX_QLM_MODE_DISABLED_SGMII: |
| case CVMX_QLM_MODE_QSGMII_SGMII: |
| return CVMX_QLM_MODE_SGMII; |
| case CVMX_QLM_MODE_QSGMII_QSGMII: |
| case CVMX_QLM_MODE_DISABLED_QSGMII: |
| case CVMX_QLM_MODE_SGMII_QSGMII: |
| return CVMX_QLM_MODE_QSGMII; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| default: |
| return qlm_mode; |
| } |
| } |
| case 1: /* PCIe */ |
| { |
| enum cvmx_qlm_mode qlm_mode1 = __cvmx_qlm_get_mode_cn70xx(1); |
| enum cvmx_qlm_mode qlm_mode2 = __cvmx_qlm_get_mode_cn70xx(2); |
| |
| switch (interface) { |
| case 0: /* PCIe0 can be DLM1 with 1, 2 or 4 lanes */ |
| return qlm_mode1; |
| case 1: |
| /* |
| * PCIe1 can be in DLM1 1 lane(1), DLM2 1 lane(0) |
| * or 2 lanes(0-1) |
| */ |
| if (qlm_mode1 == CVMX_QLM_MODE_PCIE_2X1) |
| return CVMX_QLM_MODE_PCIE_2X1; |
| else if (qlm_mode2 == CVMX_QLM_MODE_PCIE_1X2 || |
| qlm_mode2 == CVMX_QLM_MODE_PCIE_2X1) |
| return qlm_mode2; |
| else |
| return CVMX_QLM_MODE_DISABLED; |
| case 2: /* PCIe2 can be DLM2 1 lanes(1) */ |
| if (qlm_mode2 == CVMX_QLM_MODE_PCIE_2X1) |
| return qlm_mode2; |
| else |
| return CVMX_QLM_MODE_DISABLED; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| } |
| case 2: /* SATA */ |
| { |
| enum cvmx_qlm_mode qlm_mode = __cvmx_qlm_get_mode_cn70xx(2); |
| |
| if (qlm_mode == CVMX_QLM_MODE_SATA_2X1) |
| return CVMX_QLM_MODE_SATA_2X1; |
| else |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| } |
| |
| static enum cvmx_qlm_mode __cvmx_qlm_get_mode_cn6xxx(int qlm) |
| { |
| cvmx_mio_qlmx_cfg_t qlmx_cfg; |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { |
| qlmx_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(qlm)); |
| /* QLM is disabled when QLM SPD is 15. */ |
| if (qlmx_cfg.s.qlm_spd == 15) |
| return CVMX_QLM_MODE_DISABLED; |
| |
| switch (qlmx_cfg.s.qlm_cfg) { |
| case 0: /* PCIE */ |
| return CVMX_QLM_MODE_PCIE; |
| case 1: /* ILK */ |
| return CVMX_QLM_MODE_ILK; |
| case 2: /* SGMII */ |
| return CVMX_QLM_MODE_SGMII; |
| case 3: /* XAUI */ |
| return CVMX_QLM_MODE_XAUI; |
| case 7: /* RXAUI */ |
| return CVMX_QLM_MODE_RXAUI; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| } else if (OCTEON_IS_MODEL(OCTEON_CN66XX)) { |
| qlmx_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(qlm)); |
| /* QLM is disabled when QLM SPD is 15. */ |
| if (qlmx_cfg.s.qlm_spd == 15) |
| return CVMX_QLM_MODE_DISABLED; |
| |
| switch (qlmx_cfg.s.qlm_cfg) { |
| case 0x9: /* SGMII */ |
| return CVMX_QLM_MODE_SGMII; |
| case 0xb: /* XAUI */ |
| return CVMX_QLM_MODE_XAUI; |
| case 0x0: /* PCIE gen2 */ |
| case 0x8: /* PCIE gen2 (alias) */ |
| case 0x2: /* PCIE gen1 */ |
| case 0xa: /* PCIE gen1 (alias) */ |
| return CVMX_QLM_MODE_PCIE; |
| case 0x1: /* SRIO 1x4 short */ |
| case 0x3: /* SRIO 1x4 long */ |
| return CVMX_QLM_MODE_SRIO_1X4; |
| case 0x4: /* SRIO 2x2 short */ |
| case 0x6: /* SRIO 2x2 long */ |
| return CVMX_QLM_MODE_SRIO_2X2; |
| case 0x5: /* SRIO 4x1 short */ |
| case 0x7: /* SRIO 4x1 long */ |
| if (!OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0)) |
| return CVMX_QLM_MODE_SRIO_4X1; |
| fallthrough; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| } 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 = csr_rd(CVMX_GMXX_INF_MODE(0)); |
| if (inf_mode.s.speed == 15) |
| return CVMX_QLM_MODE_DISABLED; |
| else if (inf_mode.s.mode == 0) |
| return CVMX_QLM_MODE_SGMII; |
| else |
| return CVMX_QLM_MODE_XAUI; |
| } |
| status_reg.u64 = csr_rd(CVMX_SRIOX_STATUS_REG(qlm)); |
| if (status_reg.s.srio) |
| return CVMX_QLM_MODE_SRIO_1X4; |
| else |
| return CVMX_QLM_MODE_PCIE; |
| } else if (OCTEON_IS_MODEL(OCTEON_CN61XX)) { |
| qlmx_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(qlm)); |
| /* QLM is disabled when QLM SPD is 15. */ |
| if (qlmx_cfg.s.qlm_spd == 15) |
| return CVMX_QLM_MODE_DISABLED; |
| |
| switch (qlm) { |
| case 0: |
| switch (qlmx_cfg.s.qlm_cfg) { |
| case 0: /* PCIe 1x4 gen2 / gen1 */ |
| return CVMX_QLM_MODE_PCIE; |
| case 2: /* SGMII */ |
| return CVMX_QLM_MODE_SGMII; |
| case 3: /* XAUI */ |
| return CVMX_QLM_MODE_XAUI; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| case 1: |
| switch (qlmx_cfg.s.qlm_cfg) { |
| case 0: /* PCIe 1x2 gen2 / gen1 */ |
| return CVMX_QLM_MODE_PCIE_1X2; |
| case 1: /* PCIe 2x1 gen2 / gen1 */ |
| return CVMX_QLM_MODE_PCIE_2X1; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| case 2: |
| switch (qlmx_cfg.s.qlm_cfg) { |
| case 2: /* SGMII */ |
| return CVMX_QLM_MODE_SGMII; |
| case 3: /* XAUI */ |
| return CVMX_QLM_MODE_XAUI; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| } |
| } else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) { |
| qlmx_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(qlm)); |
| /* QLM is disabled when QLM SPD is 15. */ |
| if (qlmx_cfg.s.qlm_spd == 15) |
| return CVMX_QLM_MODE_DISABLED; |
| |
| switch (qlm) { |
| case 0: |
| if (qlmx_cfg.s.qlm_cfg == 2) /* SGMII */ |
| return CVMX_QLM_MODE_SGMII; |
| break; |
| case 1: |
| switch (qlmx_cfg.s.qlm_cfg) { |
| case 0: /* PCIe 1x2 gen2 / gen1 */ |
| return CVMX_QLM_MODE_PCIE_1X2; |
| case 1: /* PCIe 2x1 gen2 / gen1 */ |
| return CVMX_QLM_MODE_PCIE_2X1; |
| default: |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| } |
| } |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| |
| /** |
| * @INTERNAL |
| * Decrement the MPLL Multiplier for the DLM as per Errata G-20669 |
| * |
| * @param qlm DLM to configure |
| * @param baud_mhz Speed of the DLM configured at |
| * @param old_multiplier MPLL_MULTIPLIER value to decrement |
| */ |
| void __cvmx_qlm_set_mult(int qlm, int baud_mhz, int old_multiplier) |
| { |
| cvmx_gserx_dlmx_mpll_multiplier_t mpll_multiplier; |
| cvmx_gserx_dlmx_ref_clkdiv2_t clkdiv; |
| u64 meas_refclock, mult; |
| |
| if (!OCTEON_IS_MODEL(OCTEON_CN70XX)) |
| return; |
| |
| if (qlm == -1) |
| return; |
| |
| meas_refclock = cvmx_qlm_measure_clock(qlm); |
| if (meas_refclock == 0) { |
| printf("DLM%d: Reference clock not running\n", qlm); |
| return; |
| } |
| |
| /* |
| * The baud rate multiplier needs to be adjusted on the CN70XX if |
| * the reference clock is > 100MHz. |
| */ |
| if (qlm == 0) { |
| clkdiv.u64 = csr_rd(CVMX_GSERX_DLMX_REF_CLKDIV2(qlm, 0)); |
| if (clkdiv.s.ref_clkdiv2) |
| baud_mhz *= 2; |
| } |
| mult = (uint64_t)baud_mhz * 1000000 + (meas_refclock / 2); |
| mult /= meas_refclock; |
| |
| /* |
| * 6. Decrease MPLL_MULTIPLIER by one continually until it reaches |
| * the desired long-term setting, ensuring that each MPLL_MULTIPLIER |
| * value is constant for at least 1 msec before changing to the next |
| * value. The desired long-term setting is as indicated in HRM tables |
| * 21-1, 21-2, and 21-3. This is not required with the HRM |
| * sequence. |
| */ |
| do { |
| mpll_multiplier.u64 = csr_rd(CVMX_GSERX_DLMX_MPLL_MULTIPLIER(qlm, 0)); |
| mpll_multiplier.s.mpll_multiplier = --old_multiplier; |
| csr_wr(CVMX_GSERX_DLMX_MPLL_MULTIPLIER(qlm, 0), mpll_multiplier.u64); |
| /* Wait for 1 ms */ |
| udelay(1000); |
| } while (old_multiplier > (int)mult); |
| } |
| |
| enum cvmx_qlm_mode cvmx_qlm_get_mode_cn78xx(int node, int qlm) |
| { |
| cvmx_gserx_cfg_t gserx_cfg; |
| int qlm_mode[2][9] = { { -1, -1, -1, -1, -1, -1, -1, -1 }, |
| { -1, -1, -1, -1, -1, -1, -1, -1 } }; |
| |
| if (qlm >= 8) |
| return CVMX_QLM_MODE_OCI; |
| |
| if (qlm_mode[node][qlm] != -1) |
| return qlm_mode[node][qlm]; |
| |
| gserx_cfg.u64 = csr_rd_node(node, CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.pcie) { |
| switch (qlm) { |
| case 0: /* Either PEM0 x4 or PEM0 x8 */ |
| case 1: /* Either PEM0 x8 or PEM1 x4 */ |
| { |
| cvmx_pemx_cfg_t pemx_cfg; |
| |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(0)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM0 x8 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM0 x4 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| case 2: /* Either PEM2 x4 or PEM2 x8 */ |
| { |
| cvmx_pemx_cfg_t pemx_cfg; |
| |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(2)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM2 x8 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM2 x4 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| case 3: /* Either PEM2 x8 or PEM3 x4 or PEM3 x8 */ |
| { |
| cvmx_pemx_cfg_t pemx_cfg; |
| |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(2)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM2 x8 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } |
| |
| /* Can be first 4 lanes of PEM3 */ |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(3)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM3 x8 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM2 x4 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| case 4: /* Either PEM3 x8 or PEM3 x4 */ |
| { |
| cvmx_pemx_cfg_t pemx_cfg; |
| |
| pemx_cfg.u64 = csr_rd_node(node, CVMX_PEMX_CFG(3)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM3 x8 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM3 x4 */ |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| default: |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| } |
| } else if (gserx_cfg.s.ila) { |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_ILK; |
| } else if (gserx_cfg.s.bgx) { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| int bgx = (qlm < 2) ? qlm : qlm - 2; |
| |
| cmr_config.u64 = csr_rd_node(node, CVMX_BGXX_CMRX_CONFIG(0, bgx)); |
| pmd_control.u64 = csr_rd_node(node, CVMX_BGXX_SPUX_BR_PMD_CONTROL(0, bgx)); |
| |
| switch (cmr_config.s.lmac_type) { |
| case 0: |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_SGMII; |
| break; |
| case 1: |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_XAUI; |
| break; |
| case 2: |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_RXAUI; |
| break; |
| case 3: |
| /* |
| * Use training to determine if we're in 10GBASE-KR |
| * or XFI |
| */ |
| if (pmd_control.s.train_en) |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_10G_KR; |
| else |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_XFI; |
| break; |
| case 4: |
| /* |
| * Use training to determine if we're in 40GBASE-KR |
| * or XLAUI |
| */ |
| if (pmd_control.s.train_en) |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_40G_KR4; |
| else |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_XLAUI; |
| break; |
| default: |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| } |
| } else { |
| qlm_mode[node][qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| |
| return qlm_mode[node][qlm]; |
| } |
| |
| enum cvmx_qlm_mode __cvmx_qlm_get_mode_cn73xx(int qlm) |
| { |
| cvmx_gserx_cfg_t gserx_cfg; |
| int qlm_mode[7] = { -1, -1, -1, -1, -1, -1, -1 }; |
| |
| if (qlm_mode[qlm] != -1) |
| return qlm_mode[qlm]; |
| |
| if (qlm > 6) { |
| debug("Invalid QLM(%d) passed\n", qlm); |
| return -1; |
| } |
| |
| gserx_cfg.u64 = csr_rd(CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.pcie) { |
| cvmx_pemx_cfg_t pemx_cfg; |
| |
| switch (qlm) { |
| case 0: /* Either PEM0 x4 or PEM0 x8 */ |
| case 1: /* Either PEM0 x8 or PEM1 x4 */ |
| { |
| pemx_cfg.u64 = csr_rd(CVMX_PEMX_CFG(0)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM0 x8 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM0/PEM1 x4 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| case 2: /* Either PEM2 x4 or PEM2 x8 */ |
| { |
| pemx_cfg.u64 = csr_rd(CVMX_PEMX_CFG(2)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM2 x8 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM2 x4 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| case 5: |
| case 6: /* PEM3 x2 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE_1X2; /* PEM3 x2 */ |
| break; |
| case 3: /* Either PEM2 x8 or PEM3 x4 */ |
| { |
| pemx_cfg.u64 = csr_rd(CVMX_PEMX_CFG(2)); |
| if (pemx_cfg.cn78xx.lanes8) { |
| /* PEM2 x8 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE_1X8; |
| } else { |
| /* PEM3 x4 */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE; |
| } |
| break; |
| } |
| default: |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| } |
| } else if (gserx_cfg.s.bgx) { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_cmr_rx_lmacs_t bgx_cmr_rx_lmacs; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| int bgx = 0; |
| int start = 0, end = 4, index; |
| int lane_mask = 0, train_mask = 0; |
| int mux = 0; // 0:BGX2 (DLM5/DLM6), 1:BGX2(DLM5), 2:BGX2(DLM6) |
| |
| if (qlm < 4) { |
| bgx = qlm - 2; |
| } else if (qlm == 5 || qlm == 6) { |
| bgx = 2; |
| mux = cvmx_qlm_mux_interface(bgx); |
| if (mux == 0) { |
| start = 0; |
| end = 4; |
| } else if (mux == 1) { |
| start = 0; |
| end = 2; |
| } else if (mux == 2) { |
| start = 2; |
| end = 4; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| return qlm_mode[qlm]; |
| } |
| } |
| |
| for (index = start; index < end; index++) { |
| cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(index, bgx)); |
| pmd_control.u64 = csr_rd(CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, bgx)); |
| lane_mask |= (cmr_config.s.lmac_type << (index * 4)); |
| train_mask |= (pmd_control.s.train_en << (index * 4)); |
| } |
| |
| /* Need to include DLM5 lmacs when only DLM6 DLM is used */ |
| if (mux == 2) |
| bgx_cmr_rx_lmacs.u64 = csr_rd(CVMX_BGXX_CMR_RX_LMACS(2)); |
| switch (lane_mask) { |
| case 0: |
| if (mux == 1) { |
| qlm_mode[qlm] = CVMX_QLM_MODE_SGMII_2X1; |
| } else if (mux == 2) { |
| qlm_mode[qlm] = CVMX_QLM_MODE_SGMII_2X1; |
| bgx_cmr_rx_lmacs.s.lmacs = 4; |
| } |
| qlm_mode[qlm] = CVMX_QLM_MODE_SGMII; |
| break; |
| case 0x1: |
| qlm_mode[qlm] = CVMX_QLM_MODE_XAUI; |
| break; |
| case 0x2: |
| if (mux == 1) { |
| // NONE+RXAUI |
| qlm_mode[qlm] = CVMX_QLM_MODE_RXAUI_1X2; |
| } else if (mux == 0) { |
| // RXAUI+SGMII |
| qlm_mode[qlm] = CVMX_QLM_MODE_MIXED; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| case 0x202: |
| if (mux == 2) { |
| // RXAUI+RXAUI |
| qlm_mode[qlm] = CVMX_QLM_MODE_RXAUI_1X2; |
| bgx_cmr_rx_lmacs.s.lmacs = 4; |
| } else if (mux == 1) { |
| // RXAUI+RXAUI |
| qlm_mode[qlm] = CVMX_QLM_MODE_RXAUI_1X2; |
| } else if (mux == 0) { |
| qlm_mode[qlm] = CVMX_QLM_MODE_RXAUI; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| case 0x22: |
| qlm_mode[qlm] = CVMX_QLM_MODE_RXAUI; |
| break; |
| case 0x3333: |
| /* |
| * Use training to determine if we're in 10GBASE-KR |
| * or XFI |
| */ |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_10G_KR; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_XFI; |
| break; |
| case 0x4: |
| /* |
| * Use training to determine if we're in 40GBASE-KR |
| * or XLAUI |
| */ |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_40G_KR4; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_XLAUI; |
| break; |
| case 0x0005: |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_SGMII; |
| break; |
| case 0x3335: |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_10G_KR; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_XFI; |
| break; |
| case 0x45: |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_40G_KR4; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_XLAUI; |
| break; |
| case 0x225: |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_RXAUI; |
| break; |
| case 0x15: |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_XAUI; |
| break; |
| |
| case 0x200: |
| if (mux == 2) { |
| qlm_mode[qlm] = CVMX_QLM_MODE_RXAUI_1X2; |
| bgx_cmr_rx_lmacs.s.lmacs = 4; |
| } else |
| case 0x205: |
| case 0x233: |
| case 0x3302: |
| case 0x3305: |
| if (mux == 0) |
| qlm_mode[qlm] = CVMX_QLM_MODE_MIXED; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| case 0x3300: |
| if (mux == 0) { |
| qlm_mode[qlm] = CVMX_QLM_MODE_MIXED; |
| } else if (mux == 2) { |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_10G_KR_1X2; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_XFI_1X2; |
| bgx_cmr_rx_lmacs.s.lmacs = 4; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| case 0x33: |
| if (mux == 1 || mux == 2) { |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_10G_KR_1X2; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_XFI_1X2; |
| if (mux == 2) |
| bgx_cmr_rx_lmacs.s.lmacs = 4; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| break; |
| case 0x0035: |
| if (mux == 0) |
| qlm_mode[qlm] = CVMX_QLM_MODE_MIXED; |
| else if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_10G_KR_1X1; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_RGMII_XFI_1X1; |
| break; |
| case 0x235: |
| if (mux == 0) |
| qlm_mode[qlm] = CVMX_QLM_MODE_MIXED; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| default: |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| } |
| if (mux == 2) { |
| csr_wr(CVMX_BGXX_CMR_RX_LMACS(2), bgx_cmr_rx_lmacs.u64); |
| csr_wr(CVMX_BGXX_CMR_TX_LMACS(2), bgx_cmr_rx_lmacs.u64); |
| } |
| } else if (gserx_cfg.s.sata) { |
| qlm_mode[qlm] = CVMX_QLM_MODE_SATA_2X1; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| |
| return qlm_mode[qlm]; |
| } |
| |
| enum cvmx_qlm_mode __cvmx_qlm_get_mode_cnf75xx(int qlm) |
| { |
| cvmx_gserx_cfg_t gserx_cfg; |
| int qlm_mode[9] = { -1, -1, -1, -1, -1, -1, -1 }; |
| |
| if (qlm_mode[qlm] != -1) |
| return qlm_mode[qlm]; |
| |
| if (qlm > 9) { |
| debug("Invalid QLM(%d) passed\n", qlm); |
| return -1; |
| } |
| |
| if ((qlm == 2 || qlm == 3) && (OCTEON_IS_MODEL(OCTEON_CNF75XX))) { |
| cvmx_sriox_status_reg_t status_reg; |
| int port = (qlm == 2) ? 0 : 1; |
| |
| status_reg.u64 = csr_rd(CVMX_SRIOX_STATUS_REG(port)); |
| /* FIXME add different width */ |
| if (status_reg.s.srio) |
| qlm_mode[qlm] = CVMX_QLM_MODE_SRIO_1X4; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| return qlm_mode[qlm]; |
| } |
| |
| gserx_cfg.u64 = csr_rd(CVMX_GSERX_CFG(qlm)); |
| if (gserx_cfg.s.pcie) { |
| switch (qlm) { |
| case 0: /* Either PEM0 x2 or PEM0 x4 */ |
| case 1: /* Either PEM1 x2 or PEM0 x4 */ |
| { |
| /* FIXME later */ |
| qlm_mode[qlm] = CVMX_QLM_MODE_PCIE; |
| break; |
| } |
| default: |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| } |
| } else if (gserx_cfg.s.bgx) { |
| cvmx_bgxx_cmrx_config_t cmr_config; |
| cvmx_bgxx_spux_br_pmd_control_t pmd_control; |
| int bgx = 0; |
| int start = 0, end = 4, index; |
| int lane_mask = 0, train_mask = 0; |
| int mux = 0; // 0:BGX0 (DLM4/DLM5), 1:BGX0(DLM4), 2:BGX0(DLM5) |
| cvmx_gserx_cfg_t gser1, gser2; |
| |
| gser1.u64 = csr_rd(CVMX_GSERX_CFG(4)); |
| gser2.u64 = csr_rd(CVMX_GSERX_CFG(5)); |
| if (gser1.s.bgx && gser2.s.bgx) { |
| start = 0; |
| end = 4; |
| } else if (gser1.s.bgx) { |
| start = 0; |
| end = 2; |
| mux = 1; |
| } else if (gser2.s.bgx) { |
| start = 2; |
| end = 4; |
| mux = 2; |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| return qlm_mode[qlm]; |
| } |
| |
| for (index = start; index < end; index++) { |
| cmr_config.u64 = csr_rd(CVMX_BGXX_CMRX_CONFIG(index, bgx)); |
| pmd_control.u64 = csr_rd(CVMX_BGXX_SPUX_BR_PMD_CONTROL(index, bgx)); |
| lane_mask |= (cmr_config.s.lmac_type << (index * 4)); |
| train_mask |= (pmd_control.s.train_en << (index * 4)); |
| } |
| |
| switch (lane_mask) { |
| case 0: |
| if (mux == 1 || mux == 2) |
| qlm_mode[qlm] = CVMX_QLM_MODE_SGMII_2X1; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_SGMII; |
| break; |
| case 0x3300: |
| if (mux == 0) |
| qlm_mode[qlm] = CVMX_QLM_MODE_MIXED; |
| else if (mux == 2) |
| if (train_mask) |
| qlm_mode[qlm] = CVMX_QLM_MODE_10G_KR_1X2; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_XFI_1X2; |
| else |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| default: |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| break; |
| } |
| } else { |
| qlm_mode[qlm] = CVMX_QLM_MODE_DISABLED; |
| } |
| |
| return qlm_mode[qlm]; |
| } |
| |
| /* |
| * Read QLM and return mode. |
| */ |
| enum cvmx_qlm_mode cvmx_qlm_get_mode(int qlm) |
| { |
| if (OCTEON_IS_OCTEON2()) |
| return __cvmx_qlm_get_mode_cn6xxx(qlm); |
| else if (OCTEON_IS_MODEL(OCTEON_CN70XX)) |
| return __cvmx_qlm_get_mode_cn70xx(qlm); |
| else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) |
| return cvmx_qlm_get_mode_cn78xx(cvmx_get_node_num(), qlm); |
| else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) |
| return __cvmx_qlm_get_mode_cn73xx(qlm); |
| else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) |
| return __cvmx_qlm_get_mode_cnf75xx(qlm); |
| |
| return CVMX_QLM_MODE_DISABLED; |
| } |
| |
| int cvmx_qlm_measure_clock_cn7xxx(int node, int qlm) |
| { |
| cvmx_gserx_cfg_t cfg; |
| cvmx_gserx_refclk_sel_t refclk_sel; |
| cvmx_gserx_lane_mode_t lane_mode; |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN73XX)) { |
| if (node != 0 || qlm >= 7) |
| return -1; |
| } else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { |
| if (qlm >= 8 || node > 1) |
| return -1; /* FIXME for OCI */ |
| } else { |
| debug("%s: Unsupported OCTEON model\n", __func__); |
| return -1; |
| } |
| |
| cfg.u64 = csr_rd_node(node, CVMX_GSERX_CFG(qlm)); |
| |
| if (cfg.s.pcie) { |
| refclk_sel.u64 = csr_rd_node(node, CVMX_GSERX_REFCLK_SEL(qlm)); |
| if (refclk_sel.s.pcie_refclk125) |
| return REF_125MHZ; /* Ref 125 Mhz */ |
| else |
| return REF_100MHZ; /* Ref 100Mhz */ |
| } |
| |
| lane_mode.u64 = csr_rd_node(node, CVMX_GSERX_LANE_MODE(qlm)); |
| switch (lane_mode.s.lmode) { |
| case R_25G_REFCLK100: |
| return REF_100MHZ; |
| case R_5G_REFCLK100: |
| return REF_100MHZ; |
| case R_8G_REFCLK100: |
| return REF_100MHZ; |
| case R_125G_REFCLK15625_KX: |
| return REF_156MHZ; |
| case R_3125G_REFCLK15625_XAUI: |
| return REF_156MHZ; |
| case R_103125G_REFCLK15625_KR: |
| return REF_156MHZ; |
| case R_125G_REFCLK15625_SGMII: |
| return REF_156MHZ; |
| case R_5G_REFCLK15625_QSGMII: |
| return REF_156MHZ; |
| case R_625G_REFCLK15625_RXAUI: |
| return REF_156MHZ; |
| case R_25G_REFCLK125: |
| return REF_125MHZ; |
| case R_5G_REFCLK125: |
| return REF_125MHZ; |
| case R_8G_REFCLK125: |
| return REF_125MHZ; |
| default: |
| return 0; |
| } |
| } |
| |
| /** |
| * Measure the reference clock of a QLM on a multi-node setup |
| * |
| * @param node node to measure |
| * @param qlm QLM to measure |
| * |
| * @return Clock rate in Hz |
| */ |
| int cvmx_qlm_measure_clock_node(int node, int qlm) |
| { |
| if (octeon_has_feature(OCTEON_FEATURE_MULTINODE)) |
| return cvmx_qlm_measure_clock_cn7xxx(node, qlm); |
| else |
| return cvmx_qlm_measure_clock(qlm); |
| } |
| |
| /** |
| * Measure the reference clock of a QLM |
| * |
| * @param qlm QLM to measure |
| * |
| * @return Clock rate in Hz |
| */ |
| int cvmx_qlm_measure_clock(int qlm) |
| { |
| cvmx_mio_ptp_clock_cfg_t ptp_clock; |
| u64 count; |
| u64 start_cycle, stop_cycle; |
| int evcnt_offset = 0x10; |
| int incr_count = 1; |
| int ref_clock[16] = { 0 }; |
| |
| if (ref_clock[qlm]) |
| return ref_clock[qlm]; |
| |
| if (OCTEON_IS_OCTEON3() && !OCTEON_IS_MODEL(OCTEON_CN70XX)) |
| return cvmx_qlm_measure_clock_cn7xxx(cvmx_get_node_num(), qlm); |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN70XX) && qlm == 0) { |
| cvmx_gserx_dlmx_ref_clkdiv2_t ref_clkdiv2; |
| |
| ref_clkdiv2.u64 = csr_rd(CVMX_GSERX_DLMX_REF_CLKDIV2(qlm, 0)); |
| if (ref_clkdiv2.s.ref_clkdiv2) |
| incr_count = 2; |
| } |
| |
| /* Fix reference clock for OCI QLMs */ |
| |
| /* Disable the PTP event counter while we configure it */ |
| ptp_clock.u64 = csr_rd(CVMX_MIO_PTP_CLOCK_CFG); /* For CN63XXp1 errata */ |
| ptp_clock.s.evcnt_en = 0; |
| csr_wr(CVMX_MIO_PTP_CLOCK_CFG, ptp_clock.u64); |
| |
| /* Count on rising edge, Choose which QLM to count */ |
| ptp_clock.u64 = csr_rd(CVMX_MIO_PTP_CLOCK_CFG); /* For CN63XXp1 errata */ |
| ptp_clock.s.evcnt_edge = 0; |
| ptp_clock.s.evcnt_in = evcnt_offset + qlm; |
| csr_wr(CVMX_MIO_PTP_CLOCK_CFG, ptp_clock.u64); |
| |
| /* Clear MIO_PTP_EVT_CNT */ |
| csr_rd(CVMX_MIO_PTP_EVT_CNT); /* For CN63XXp1 errata */ |
| count = csr_rd(CVMX_MIO_PTP_EVT_CNT); |
| csr_wr(CVMX_MIO_PTP_EVT_CNT, -count); |
| |
| /* Set MIO_PTP_EVT_CNT to 1 billion */ |
| csr_wr(CVMX_MIO_PTP_EVT_CNT, 1000000000); |
| |
| /* Enable the PTP event counter */ |
| ptp_clock.u64 = csr_rd(CVMX_MIO_PTP_CLOCK_CFG); /* For CN63XXp1 errata */ |
| ptp_clock.s.evcnt_en = 1; |
| csr_wr(CVMX_MIO_PTP_CLOCK_CFG, ptp_clock.u64); |
| |
| start_cycle = get_ticks(); |
| /* Wait for 50ms */ |
| mdelay(50); |
| |
| /* Read the counter */ |
| csr_rd(CVMX_MIO_PTP_EVT_CNT); /* For CN63XXp1 errata */ |
| count = csr_rd(CVMX_MIO_PTP_EVT_CNT); |
| stop_cycle = get_ticks(); |
| |
| /* Disable the PTP event counter */ |
| ptp_clock.u64 = csr_rd(CVMX_MIO_PTP_CLOCK_CFG); /* For CN63XXp1 errata */ |
| ptp_clock.s.evcnt_en = 0; |
| csr_wr(CVMX_MIO_PTP_CLOCK_CFG, ptp_clock.u64); |
| |
| /* Clock counted down, so reverse it */ |
| count = 1000000000 - count; |
| count *= incr_count; |
| |
| /* Return the rate */ |
| ref_clock[qlm] = count * gd->cpu_clk / (stop_cycle - start_cycle); |
| |
| return ref_clock[qlm]; |
| } |
| |
| /* |
| * Perform RX equalization on a QLM |
| * |
| * @param node Node the QLM is on |
| * @param qlm QLM to perform RX equalization on |
| * @param lane Lane to use, or -1 for all lanes |
| * |
| * @return Zero on success, negative if any lane failed RX equalization |
| */ |
| int __cvmx_qlm_rx_equalization(int node, int qlm, int lane) |
| { |
| cvmx_gserx_phy_ctl_t phy_ctl; |
| cvmx_gserx_br_rxx_ctl_t rxx_ctl; |
| cvmx_gserx_br_rxx_eer_t rxx_eer; |
| cvmx_gserx_rx_eie_detsts_t eie_detsts; |
| int fail, gbaud, l, lane_mask; |
| enum cvmx_qlm_mode mode; |
| int max_lanes = cvmx_qlm_get_lanes(qlm); |
| cvmx_gserx_lane_mode_t lmode; |
| cvmx_gserx_lane_px_mode_1_t pmode_1; |
| int pending = 0; |
| u64 timeout; |
| |
| /* Don't touch QLMs if it is reset or powered down */ |
| phy_ctl.u64 = csr_rd_node(node, CVMX_GSERX_PHY_CTL(qlm)); |
| if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset) |
| return -1; |
| |
| /* |
| * Check whether GSER PRBS pattern matcher is enabled on any of the |
| * applicable lanes. Can't complete RX Equalization while pattern |
| * matcher is enabled because it causes errors |
| */ |
| for (l = 0; l < max_lanes; l++) { |
| cvmx_gserx_lanex_lbert_cfg_t lbert_cfg; |
| |
| if (lane != -1 && lane != l) |
| continue; |
| |
| lbert_cfg.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_LBERT_CFG(l, qlm)); |
| if (lbert_cfg.s.lbert_pm_en == 1) |
| return -1; |
| } |
| |
| /* Get Lane Mode */ |
| lmode.u64 = csr_rd_node(node, CVMX_GSERX_LANE_MODE(qlm)); |
| |
| /* |
| * Check to see if in VMA manual mode is set. If in VMA manual mode |
| * don't complete rx equalization |
| */ |
| pmode_1.u64 = csr_rd_node(node, CVMX_GSERX_LANE_PX_MODE_1(lmode.s.lmode, qlm)); |
| if (pmode_1.s.vma_mm == 1) { |
| #ifdef DEBUG_QLM |
| debug("N%d:QLM%d: VMA Manual (manual DFE) selected. Not completing Rx equalization\n", |
| node, qlm); |
| #endif |
| return 0; |
| } |
| |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX)) { |
| gbaud = cvmx_qlm_get_gbaud_mhz_node(node, qlm); |
| mode = cvmx_qlm_get_mode_cn78xx(node, qlm); |
| } else { |
| gbaud = cvmx_qlm_get_gbaud_mhz(qlm); |
| mode = cvmx_qlm_get_mode(qlm); |
| } |
| |
| /* Apply RX Equalization for speed >= 8G */ |
| if (qlm < 8) { |
| if (gbaud < 6250) |
| return 0; |
| } |
| |
| /* Don't run on PCIe Links */ |
| if (mode == CVMX_QLM_MODE_PCIE || mode == CVMX_QLM_MODE_PCIE_1X8 || |
| mode == CVMX_QLM_MODE_PCIE_1X2 || mode == CVMX_QLM_MODE_PCIE_2X1) |
| return -1; |
| |
| fail = 0; |
| |
| /* |
| * Before completing Rx equalization wait for |
| * GSERx_RX_EIE_DETSTS[CDRLOCK] to be set. |
| * This ensures the rx data is valid |
| */ |
| if (lane == -1) { |
| /* |
| * check all 4 Lanes (cdrlock = 1111/b) for CDR Lock with |
| * lane == -1 |
| */ |
| if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_GSERX_RX_EIE_DETSTS(qlm), |
| cvmx_gserx_rx_eie_detsts_t, cdrlock, ==, |
| (1 << max_lanes) - 1, 500)) { |
| #ifdef DEBUG_QLM |
| eie_detsts.u64 = csr_rd_node(node, CVMX_GSERX_RX_EIE_DETSTS(qlm)); |
| debug("ERROR: %d:QLM%d: CDR Lock not detected for all 4 lanes. CDR_LOCK(0x%x)\n", |
| node, qlm, eie_detsts.s.cdrlock); |
| #endif |
| return -1; |
| } |
| } else { |
| if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_GSERX_RX_EIE_DETSTS(qlm), |
| cvmx_gserx_rx_eie_detsts_t, cdrlock, &, (1 << lane), |
| 500)) { |
| #ifdef DEBUG_QLM |
| eie_detsts.u64 = csr_rd_node(node, CVMX_GSERX_RX_EIE_DETSTS(qlm)); |
| debug("ERROR: %d:QLM%d: CDR Lock not detected for Lane%d CDR_LOCK(0x%x)\n", |
| node, qlm, lane, eie_detsts.s.cdrlock); |
| #endif |
| return -1; |
| } |
| } |
| |
| /* |
| * Errata (GSER-20075) GSER(0..13)_BR_RX3_EER[RXT_ERR] is |
| * GSER(0..13)_BR_RX2_EER[RXT_ERR]. Since lanes 2-3 trigger at the |
| * same time, we need to setup lane 3 before we loop through the lanes |
| */ |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && (lane == -1 || lane == 3)) { |
| /* Enable software control */ |
| rxx_ctl.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_CTL(3, qlm)); |
| rxx_ctl.s.rxt_swm = 1; |
| csr_wr_node(node, CVMX_GSERX_BR_RXX_CTL(3, qlm), rxx_ctl.u64); |
| |
| /* Clear the completion flag */ |
| rxx_eer.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_EER(3, qlm)); |
| rxx_eer.s.rxt_esv = 0; |
| csr_wr_node(node, CVMX_GSERX_BR_RXX_EER(3, qlm), rxx_eer.u64); |
| /* Initiate a new request on lane 2 */ |
| if (lane == 3) { |
| rxx_eer.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_EER(2, qlm)); |
| rxx_eer.s.rxt_eer = 1; |
| csr_wr_node(node, CVMX_GSERX_BR_RXX_EER(2, qlm), rxx_eer.u64); |
| } |
| } |
| |
| for (l = 0; l < max_lanes; l++) { |
| if (lane != -1 && lane != l) |
| continue; |
| |
| /* |
| * Skip lane 3 on 78p1.x due to Errata (GSER-20075). |
| * Handled above |
| */ |
| if (OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X) && l == 3) { |
| /* |
| * Need to add lane 3 to pending list for 78xx |
| * pass 1.x |
| */ |
| pending |= 1 << 3; |
| continue; |
| } |
| /* Enable software control */ |
| rxx_ctl.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_CTL(l, qlm)); |
| rxx_ctl.s.rxt_swm = 1; |
| csr_wr_node(node, CVMX_GSERX_BR_RXX_CTL(l, qlm), rxx_ctl.u64); |
| |
| /* Clear the completion flag and initiate a new request */ |
| rxx_eer.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_EER(l, qlm)); |
| rxx_eer.s.rxt_esv = 0; |
| rxx_eer.s.rxt_eer = 1; |
| csr_wr_node(node, CVMX_GSERX_BR_RXX_EER(l, qlm), rxx_eer.u64); |
| pending |= 1 << l; |
| } |
| |
| /* |
| * Wait for 250ms, approx 10x times measured value, as XFI/XLAUI |
| * can take 21-23ms, other interfaces can take 2-3ms. |
| */ |
| timeout = get_timer(0); |
| |
| lane_mask = 0; |
| while (pending) { |
| /* Wait for RX equalization to complete */ |
| for (l = 0; l < max_lanes; l++) { |
| lane_mask = 1 << l; |
| /* Only check lanes that are pending */ |
| if (!(pending & lane_mask)) |
| continue; |
| |
| /* |
| * Read the registers for checking Electrical Idle/CDR |
| * lock and the status of the RX equalization |
| */ |
| eie_detsts.u64 = csr_rd_node(node, CVMX_GSERX_RX_EIE_DETSTS(qlm)); |
| rxx_eer.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_EER(l, qlm)); |
| |
| /* |
| * Mark failure if lane entered Electrical Idle or lost |
| * CDR Lock. The bit for the lane will have cleared in |
| * either EIESTS or CDRLOCK |
| */ |
| if (!(eie_detsts.s.eiests & eie_detsts.s.cdrlock & lane_mask)) { |
| fail |= lane_mask; |
| pending &= ~lane_mask; |
| } else if (rxx_eer.s.rxt_esv) { |
| pending &= ~lane_mask; |
| } |
| } |
| |
| /* Breakout of the loop on timeout */ |
| if (get_timer(timeout) > 250) |
| break; |
| } |
| |
| lane_mask = 0; |
| /* Cleanup and report status */ |
| for (l = 0; l < max_lanes; l++) { |
| if (lane != -1 && lane != l) |
| continue; |
| |
| lane_mask = 1 << l; |
| rxx_eer.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_EER(l, qlm)); |
| /* Switch back to hardware control */ |
| rxx_ctl.u64 = csr_rd_node(node, CVMX_GSERX_BR_RXX_CTL(l, qlm)); |
| rxx_ctl.s.rxt_swm = 0; |
| csr_wr_node(node, CVMX_GSERX_BR_RXX_CTL(l, qlm), rxx_ctl.u64); |
| |
| /* Report status */ |
| if (fail & lane_mask) { |
| #ifdef DEBUG_QLM |
| debug("%d:QLM%d: Lane%d RX equalization lost CDR Lock or entered Electrical Idle\n", |
| node, qlm, l); |
| #endif |
| } else if ((pending & lane_mask) || !rxx_eer.s.rxt_esv) { |
| #ifdef DEBUG_QLM |
| debug("%d:QLM%d: Lane %d RX equalization timeout\n", node, qlm, l); |
| #endif |
| fail |= 1 << l; |
| } else { |
| #ifdef DEBUG_QLM |
| char *dir_label[4] = { "Hold", "Inc", "Dec", "Hold" }; |
| #ifdef DEBUG_QLM_RX |
| cvmx_gserx_lanex_rx_aeq_out_0_t rx_aeq_out_0; |
| cvmx_gserx_lanex_rx_aeq_out_1_t rx_aeq_out_1; |
| cvmx_gserx_lanex_rx_aeq_out_2_t rx_aeq_out_2; |
| cvmx_gserx_lanex_rx_vma_status_0_t rx_vma_status_0; |
| #endif |
| debug("%d:QLM%d: Lane%d: RX equalization completed.\n", node, qlm, l); |
| debug(" Tx Direction Hints TXPRE: %s, TXMAIN: %s, TXPOST: %s, Figure of Merit: %d\n", |
| dir_label[(rxx_eer.s.rxt_esm) & 0x3], |
| dir_label[((rxx_eer.s.rxt_esm) >> 2) & 0x3], |
| dir_label[((rxx_eer.s.rxt_esm) >> 4) & 0x3], rxx_eer.s.rxt_esm >> 6); |
| |
| #ifdef DEBUG_QLM_RX |
| rx_aeq_out_0.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_RX_AEQ_OUT_0(l, qlm)); |
| rx_aeq_out_1.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_RX_AEQ_OUT_1(l, qlm)); |
| rx_aeq_out_2.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_RX_AEQ_OUT_2(l, qlm)); |
| rx_vma_status_0.u64 = |
| csr_rd_node(node, CVMX_GSERX_LANEX_RX_VMA_STATUS_0(l, qlm)); |
| debug(" DFE Tap1:%lu, Tap2:%ld, Tap3:%ld, Tap4:%ld, Tap5:%ld\n", |
| (unsigned int long)cvmx_bit_extract(rx_aeq_out_1.u64, 0, 5), |
| (unsigned int long)cvmx_bit_extract_smag(rx_aeq_out_1.u64, 5, 9), |
| (unsigned int long)cvmx_bit_extract_smag(rx_aeq_out_1.u64, 10, 14), |
| (unsigned int long)cvmx_bit_extract_smag(rx_aeq_out_0.u64, 0, 4), |
| (unsigned int long)cvmx_bit_extract_smag(rx_aeq_out_0.u64, 5, 9)); |
| debug(" Pre-CTLE Gain:%lu, Post-CTLE Gain:%lu, CTLE Peak:%lu, CTLE Pole:%lu\n", |
| (unsigned int long)cvmx_bit_extract(rx_aeq_out_2.u64, 4, 4), |
| (unsigned int long)cvmx_bit_extract(rx_aeq_out_2.u64, 0, 4), |
| (unsigned int long)cvmx_bit_extract(rx_vma_status_0.u64, 2, 4), |
| (unsigned int long)cvmx_bit_extract(rx_vma_status_0.u64, 0, 2)); |
| #endif |
| #endif |
| } |
| } |
| |
| return (fail) ? -1 : 0; |
| } |
| |
| /** |
| * Errata GSER-27882 -GSER 10GBASE-KR Transmit Equalizer |
| * Training may not update PHY Tx Taps. This function is not static |
| * so we can share it with BGX KR |
| * |
| * @param node Node to apply errata workaround |
| * @param qlm QLM to apply errata workaround |
| * @param lane Lane to apply the errata |
| */ |
| int cvmx_qlm_gser_errata_27882(int node, int qlm, int lane) |
| { |
| cvmx_gserx_lanex_pcs_ctlifc_0_t clifc0; |
| cvmx_gserx_lanex_pcs_ctlifc_2_t clifc2; |
| |
| if (!(OCTEON_IS_MODEL(OCTEON_CN73XX_PASS1_0) || OCTEON_IS_MODEL(OCTEON_CN73XX_PASS1_1) || |
| OCTEON_IS_MODEL(OCTEON_CN73XX_PASS1_2) || OCTEON_IS_MODEL(OCTEON_CNF75XX_PASS1_0) || |
| OCTEON_IS_MODEL(OCTEON_CN78XX))) |
| return 0; |
| |
| if (CVMX_WAIT_FOR_FIELD64_NODE(node, CVMX_GSERX_RX_EIE_DETSTS(qlm), |
| cvmx_gserx_rx_eie_detsts_t, cdrlock, &, |
| (1 << lane), 200)) |
| return -1; |
| |
| clifc0.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_0(lane, qlm)); |
| clifc0.s.cfg_tx_coeff_req_ovrrd_val = 1; |
| csr_wr_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_0(lane, qlm), clifc0.u64); |
| clifc2.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm)); |
| clifc2.s.cfg_tx_coeff_req_ovrrd_en = 1; |
| csr_wr_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm), clifc2.u64); |
| clifc2.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm)); |
| clifc2.s.ctlifc_ovrrd_req = 1; |
| csr_wr_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm), clifc2.u64); |
| clifc2.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm)); |
| clifc2.s.cfg_tx_coeff_req_ovrrd_en = 0; |
| csr_wr_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm), clifc2.u64); |
| clifc2.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm)); |
| clifc2.s.ctlifc_ovrrd_req = 1; |
| csr_wr_node(node, CVMX_GSERX_LANEX_PCS_CTLIFC_2(lane, qlm), clifc2.u64); |
| return 0; |
| } |
| |
| /** |
| * Updates the RX EQ Default Settings Update (CTLE Bias) to support longer |
| * SERDES channels |
| * |
| * @INTERNAL |
| * |
| * @param node Node number to configure |
| * @param qlm QLM number to configure |
| */ |
| void cvmx_qlm_gser_errata_25992(int node, int qlm) |
| { |
| int lane; |
| int num_lanes = cvmx_qlm_get_lanes(qlm); |
| |
| if (!(OCTEON_IS_MODEL(OCTEON_CN73XX_PASS1_0) || OCTEON_IS_MODEL(OCTEON_CN73XX_PASS1_1) || |
| OCTEON_IS_MODEL(OCTEON_CN73XX_PASS1_2) || OCTEON_IS_MODEL(OCTEON_CN78XX_PASS1_X))) |
| return; |
| |
| for (lane = 0; lane < num_lanes; lane++) { |
| cvmx_gserx_lanex_rx_ctle_ctrl_t rx_ctle_ctrl; |
| cvmx_gserx_lanex_rx_cfg_4_t rx_cfg_4; |
| |
| rx_ctle_ctrl.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_RX_CTLE_CTRL(lane, qlm)); |
| rx_ctle_ctrl.s.pcs_sds_rx_ctle_bias_ctrl = 3; |
| csr_wr_node(node, CVMX_GSERX_LANEX_RX_CTLE_CTRL(lane, qlm), rx_ctle_ctrl.u64); |
| |
| rx_cfg_4.u64 = csr_rd_node(node, CVMX_GSERX_LANEX_RX_CFG_4(lane, qlm)); |
| rx_cfg_4.s.cfg_rx_errdet_ctrl = 0xcd6f; |
| csr_wr_node(node, CVMX_GSERX_LANEX_RX_CFG_4(lane, qlm), rx_cfg_4.u64); |
| } |
| } |
| |
| void cvmx_qlm_display_registers(int qlm) |
| { |
| int num_lanes = cvmx_qlm_get_lanes(qlm); |
| int lane; |
| const __cvmx_qlm_jtag_field_t *ptr = cvmx_qlm_jtag_get_field(); |
| |
| debug("%29s", "Field[<stop bit>:<start bit>]"); |
| for (lane = 0; lane < num_lanes; lane++) |
| debug("\t Lane %d", lane); |
| debug("\n"); |
| |
| while (ptr && ptr->name) { |
| debug("%20s[%3d:%3d]", ptr->name, ptr->stop_bit, ptr->start_bit); |
| for (lane = 0; lane < num_lanes; lane++) { |
| u64 val; |
| int tx_byp = 0; |
| |
| /* |
| * Make sure serdes_tx_byp is set for displaying |
| * TX amplitude and TX demphasis field values. |
| */ |
| if (strncmp(ptr->name, "biasdrv_", 8) == 0 || |
| strncmp(ptr->name, "tcoeff_", 7) == 0) { |
| tx_byp = cvmx_qlm_jtag_get(qlm, lane, "serdes_tx_byp"); |
| if (tx_byp == 0) { |
| debug("\t \t"); |
| continue; |
| } |
| } |
| val = cvmx_qlm_jtag_get(qlm, lane, ptr->name); |
| debug("\t%4llu (0x%04llx)", (unsigned long long)val, |
| (unsigned long long)val); |
| } |
| debug("\n"); |
| ptr++; |
| } |
| } |
| |
| /* ToDo: CVMX_DUMP_GSER removed for now (unused!) */ |