aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/cavium/bdk/libbdk-hal/qlm
diff options
context:
space:
mode:
authorDavid Hendricks <dhendricks@fb.com>2018-03-09 14:30:38 -0800
committerPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2018-07-03 15:53:32 +0000
commit7d48ac5c7dfb52fc470bbad1013b4d460bc6a1e0 (patch)
tree42002ba1e86627339ff4a6cf38efb4b3f00033bb /src/vendorcode/cavium/bdk/libbdk-hal/qlm
parentd837e660074e0621d63f59515f933c209441b653 (diff)
soc/cavium: Integrate BDK files into coreboot
* Make it compile. * Fix whitespace errors. * Fix printf formats. * Add missing headers includes * Guard headers with ifdefs Compile DRAM init code in romstage. Compile QLM, PCIe, RNG, PHY, GPIO, MDIO init code in ramstage. Change-Id: I0a93219a14bfb6ebe41103a825d5032b11e7f2c6 Signed-off-by: David Hendricks <dhendricks@fb.com> Reviewed-on: https://review.coreboot.org/25089 Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/qlm')
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c1003
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common-sata.c625
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common.c1636
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.c398
-rw-r--r--src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-margin-cn8xxx.c271
5 files changed, 3933 insertions, 0 deletions
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c
new file mode 100644
index 0000000000..303b276a8b
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c
@@ -0,0 +1,1003 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 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**************************************/
+#include <bdk.h>
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/qlm/bdk-qlm-common.h"
+#include "libbdk-arch/bdk-csrs-bgx.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-arch/bdk-csrs-sata.h"
+#include "libbdk-arch/bdk-csrs-rst.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.h"
+#include "libbdk-hal/bdk-gpio.h"
+
+/**
+ * Return the number of QLMs supported for the chip
+ *
+ * @return Number of QLMs
+ */
+int bdk_qlm_get_num(bdk_node_t node)
+{
+ return 4; /* 4 DLM */
+}
+
+/**
+ * Return the number of lanes in a QLM. QLMs normally contain
+ * 4 lanes, except for chips which only have half of a QLM.
+ *
+ * @param qlm QLM to get lanes number for
+ *
+ * @return Number of lanes on the QLM
+ */
+int bdk_qlm_get_lanes(bdk_node_t node, int qlm)
+{
+
+ if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+ return 1; /* DLM0 and DLM1 are a single lane on CN80XX */
+ else
+ return 2; /* DLMs */
+}
+
+/**
+ * Lookup the hardware QLM number for a given interface type and index. This
+ * function will fail with a fatal error if called on invalid interfaces for
+ * a chip. It returns the QLM number for an interface without checking to
+ * see if the QLM is in the correct mode.
+ *
+ * @param iftype Interface type
+ * @param interface Interface index number
+ *
+ * @return QLM number. Dies on a fatal error on failure.
+ */
+int bdk_qlm_get_qlm_num(bdk_node_t node, bdk_if_t iftype, int interface, int index)
+{
+ switch (iftype)
+ {
+ case BDK_IF_BGX:
+ {
+ int qlm;
+ switch (interface)
+ {
+ case 0:
+ {
+ /* This BGX spans two DLMs. The index must be used to
+ figure out which DLM we are using */
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(0));
+ if (gserx_cfg.s.bgx)
+ {
+ if (gserx_cfg.s.bgx_quad) /* 4 lanes together */
+ qlm = 0;
+ else if (gserx_cfg.s.bgx_dual) /* 2 lanes together */
+ qlm = (index >= 1) ? 1 : 0;
+ else /* All lanes independent */
+ {
+ bdk_qlm_modes_t mode = bdk_qlm_get_mode(node, 0);
+ if (mode == BDK_QLM_MODE_QSGMII_4X1)
+ qlm = 0;
+ else if (mode <= BDK_QLM_MODE_PCIE_1X8)
+ qlm = 1;
+ else if (cavium_is_altpkg(CAVIUM_CN81XX))
+ {
+ bdk_qlm_modes_t mode1 = bdk_qlm_get_mode(node, 1);
+ if ((mode1 != BDK_QLM_MODE_QSGMII_4X1) && (index >= 2))
+ return -1;
+ qlm = (index >= 1) ? 1 : 0;
+ }
+ else
+ qlm = (index >= 2) ? 1 : 0;
+ }
+ }
+ else
+ qlm = 1;
+ break;
+ }
+ case 1:
+ {
+ /* This BGX spans two DLMs. The index must be used to
+ figure out which DLM we are using */
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(2));
+ if (gserx_cfg.s.bgx)
+ {
+ if (gserx_cfg.s.bgx_quad) /* 4 lanes together */
+ qlm = 2;
+ else if (gserx_cfg.s.bgx_dual) /* 2 lanes together */
+ qlm = (index >= 1) ? 3 : 2;
+ else /* All lanes independent */
+ {
+ bdk_qlm_modes_t mode = bdk_qlm_get_mode(node, 2);
+ if (mode == BDK_QLM_MODE_QSGMII_4X1)
+ qlm = 2;
+ else if (mode <= BDK_QLM_MODE_PCIE_1X8)
+ qlm = 1;
+ else
+ qlm = (index >= 2) ? 3 : 2;
+ }
+ }
+ else
+ qlm = 3;
+ break;
+ }
+ default:
+ return -1;
+ }
+ /* Make sure the QLM is powered up and out of reset */
+ BDK_CSR_INIT(phy_ctl, node, BDK_GSERX_PHY_CTL(qlm));
+ if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset)
+ return -1;
+ /* Make sure the QLM is in BGX mode */
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+ if (gserx_cfg.s.bgx)
+ return qlm;
+ else
+ return -1;
+ }
+ case BDK_IF_PCIE: /* PCIe */
+ {
+ switch (interface)
+ {
+ case 0: /* PEM0 */
+ {
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(0));
+ if (gserx_cfg.s.pcie)
+ return 0; /* PEM0 is on DLM0 */
+ else
+ return -1; /* PEM0 is disabled */
+ }
+ case 1: /* PEM1 */
+ {
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(2));
+ if (gserx_cfg.s.pcie)
+ return 2; /* PEM1 is on DLM2 */
+ else
+ return -1; /* PEM1 is disabled */
+ }
+ case 2: /* PEM2 */
+ {
+ BDK_CSR_INIT(pem1_cfg, node, BDK_PEMX_CFG(1));
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(3));
+ if (!pem1_cfg.cn81xx.lanes4 && gserx_cfg.s.pcie)
+ return 3; /* PEM2 is on DLM3 */
+ else
+ return -1; /* PEM2 is disabled */
+ }
+ default: /* Max of 3 PEMs, 0-2 */
+ return -1;
+ }
+ }
+ default: /* Not supported by CN81XX */
+ return -1;
+ }
+}
+
+/**
+ * Get the mode of a QLM as a human readable string
+ *
+ * @param qlm QLM to examine
+ *
+ * @return String mode
+ */
+bdk_qlm_modes_t bdk_qlm_get_mode(bdk_node_t node, int qlm)
+{
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+ if (gserx_cfg.s.pcie)
+ {
+ switch (qlm)
+ {
+ case 0: /* PEM0 */
+ {
+ BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(0));
+ if (cavium_is_altpkg(CAVIUM_CN81XX))
+ return BDK_QLM_MODE_PCIE_1X1; /* PEM0 x1 */
+ else if (pemx_cfg.cn81xx.lanes4)
+ return BDK_QLM_MODE_PCIE_1X4; /* PEM0 x4 */
+ else
+ return BDK_QLM_MODE_PCIE_1X2; /* PEM0 x2 */
+ }
+ case 1: /* PEM0 second two lanes */
+ return BDK_QLM_MODE_PCIE_1X4; /* PEM0 x4 */
+ case 2: /* Either PEM1 x4 or PEM1 x2 */
+ {
+ BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(1));
+ if (pemx_cfg.cn81xx.lanes4)
+ return BDK_QLM_MODE_PCIE_1X4; /* PEM1 x4 */
+ else
+ return BDK_QLM_MODE_PCIE_1X2; /* PEM1 x2 */
+ }
+ case 3: /* Either PEM1 x4 or PEM2 x2 */
+ {
+ /* Can be last 2 lanes of PEM1 */
+ BDK_CSR_INIT(pem1_cfg, node, BDK_PEMX_CFG(1));
+ if (pem1_cfg.cn81xx.lanes4)
+ return BDK_QLM_MODE_PCIE_1X4; /* PEM1 x4 */
+ /* Can be 2 lanes of PEM2 */
+ return BDK_QLM_MODE_PCIE_1X2; /* PEM2 x2 */
+ }
+ default:
+ return BDK_QLM_MODE_DISABLED;
+ }
+ }
+ else if (gserx_cfg.s.bgx)
+ {
+ int bgx;
+ int bgx_index;
+ switch (qlm)
+ {
+ case 0:
+ {
+ bgx = 0;
+ bgx_index = 0;
+ break;
+ }
+ case 1:
+ bgx = 0;
+ bgx_index = 2;
+ break;
+ case 2:
+ {
+ bgx = 1;
+ bgx_index = 0;
+ break;
+ }
+ case 3:
+ bgx = 1;
+ bgx_index = 2;
+ break;
+ default:
+ return BDK_QLM_MODE_DISABLED;
+ }
+ BDK_CSR_INIT(cmrx_config, node, BDK_BGXX_CMRX_CONFIG(bgx, bgx_index));
+ bool is_kr = __bdk_qlm_is_lane_kr(node, qlm, 0);
+ switch (cmrx_config.s.lmac_type)
+ {
+ case BDK_BGX_LMAC_TYPES_E_SGMII:
+ if (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2))
+ return BDK_QLM_MODE_SGMII_1X1;
+ else
+ return BDK_QLM_MODE_SGMII_2X1;
+ case BDK_BGX_LMAC_TYPES_E_XAUI: return BDK_QLM_MODE_XAUI_1X4; /* Doesn't differntiate between XAUI and DXAUI */
+ case BDK_BGX_LMAC_TYPES_E_RXAUI: return BDK_QLM_MODE_RXAUI_1X2;
+ case BDK_BGX_LMAC_TYPES_E_TENG_R:
+ if (is_kr)
+ return (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2)) ? BDK_QLM_MODE_10G_KR_1X1 : BDK_QLM_MODE_10G_KR_2X1;
+ else
+ return (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_XFI_2X1;
+ case BDK_BGX_LMAC_TYPES_E_FORTYG_R:
+ if (is_kr)
+ return BDK_QLM_MODE_40G_KR4_1X4;
+ else
+ return BDK_QLM_MODE_XLAUI_1X4;
+ case BDK_BGX_LMAC_TYPES_E_QSGMII: return BDK_QLM_MODE_QSGMII_4X1;
+ default: return BDK_QLM_MODE_DISABLED;
+ }
+ }
+ else if (gserx_cfg.s.sata)
+ return BDK_QLM_MODE_SATA_2X1;
+ else
+ return BDK_QLM_MODE_DISABLED;
+}
+
+static int qlm_set_sata(bdk_node_t node, int qlm, bdk_qlm_modes_t mode, int baud_mhz, bdk_qlm_mode_flags_t flags)
+{
+ /* SATA has a fixed mapping for ports on CN81XX */
+ int sata_port;
+ switch (qlm)
+ {
+ case 3: /* SATA 0-1 = DLM3 lanes 0-1 */
+ sata_port = 0;
+ break;
+ default:
+ bdk_error("Attempted to configure SATA on QLM that doesn't support it\n");
+ return -1;
+ }
+ return __bdk_qlm_set_sata_cn8xxx(node, qlm, baud_mhz, sata_port, sata_port + 1);
+}
+
+/**
+ * For chips that don't use pin strapping, this function programs
+ * the QLM to the specified mode
+ *
+ * @param node Node to use in a Numa setup
+ * @param qlm QLM to configure
+ * @param mode Desired mode
+ * @param baud_mhz Desired speed
+ * @param flags Flags to specify mode specific options
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_set_mode(bdk_node_t node, int qlm, bdk_qlm_modes_t mode, int baud_mhz, bdk_qlm_mode_flags_t flags)
+{
+ int lane_mode = 0xf;
+ int lmac_type = -1;
+ int is_pcie = 0;
+ int is_sata = 0;
+ int is_ilk = 0;
+ int is_bgx = 0;
+ int bgx_block;
+ int bgx_index;
+
+ switch (qlm)
+ {
+ case 0:
+ bgx_block = 0;
+ bgx_index = 0;
+ break;
+ case 1:
+ bgx_block = 0;
+ bgx_index = 2;
+ break;
+ case 2:
+ bgx_block = 1;
+ bgx_index = 0;
+ break;
+ case 3:
+ bgx_block = 1;
+ bgx_index = 2;
+ break;
+ default:
+ bgx_block = -1;
+ bgx_index = -1;
+ break;
+ }
+
+ int measured_ref = bdk_qlm_measure_clock(node, qlm);
+ int ref_clk = (mode == BDK_QLM_MODE_DISABLED) ? 0 : __bdk_qlm_round_refclock(node, qlm, measured_ref);
+ int kr_mode = 0;
+
+ switch (mode)
+ {
+ case BDK_QLM_MODE_PCIE_1X1:
+ case BDK_QLM_MODE_PCIE_1X2:
+ case BDK_QLM_MODE_PCIE_1X4:
+ {
+ /* Note: PCIe ignores baud_mhz. Use the GEN 1/2/3 flags
+ to control speed */
+ is_pcie = 1;
+ if (ref_clk == REF_100MHZ)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+ c.s.pcie_refclk125 = 0);
+ if (baud_mhz == 2500)
+ lane_mode = BDK_GSER_LMODE_E_R_25G_REFCLK100;
+ else if (baud_mhz == 5000)
+ lane_mode = BDK_GSER_LMODE_E_R_5G_REFCLK100;
+ else
+ lane_mode = BDK_GSER_LMODE_E_R_8G_REFCLK100;
+ }
+ else if (ref_clk == REF_125MHZ)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+ c.s.pcie_refclk125 = 1);
+ if (baud_mhz == 2500)
+ lane_mode = BDK_GSER_LMODE_E_R_25G_REFCLK125;
+ else if (baud_mhz == 5000)
+ lane_mode = BDK_GSER_LMODE_E_R_5G_REFCLK125;
+ else
+ lane_mode = BDK_GSER_LMODE_E_R_8G_REFCLK125;
+ }
+ else
+ {
+ bdk_error("Invalid reference clock for PCIe on QLM%d\n", qlm);
+ return -1;
+ }
+ int cfg_md;
+ if (baud_mhz == 2500)
+ cfg_md = 0; /* Gen1 Speed */
+ else if (baud_mhz == 5000)
+ cfg_md = 1; /* Gen2 Speed */
+ else
+ cfg_md = 2; /* Gen3 Speed */
+ switch (qlm)
+ {
+ case 0: /* Either PEM0 x4 or PEM0 x2 or PEM0 x1 */
+ BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(0),
+ c.s.soft_prst = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT));
+ __bdk_qlm_setup_pem_reset(node, 0, flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+ BDK_CSR_MODIFY(c, node, BDK_PEMX_CFG(0),
+ c.cn81xx.lanes4 = (mode == BDK_QLM_MODE_PCIE_1X4);
+ //c.cn81xx.hostmd = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+ c.cn81xx.md = cfg_md);
+ break;
+ case 1: /* Second two lanes for PEM0 x4 */
+ /* PEMX_CFG already setup */
+ break;
+ case 2: /* Either PEM1 x4 or PEM1 x2 */
+ BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(1),
+ c.s.soft_prst = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT));
+ __bdk_qlm_setup_pem_reset(node, 1, flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+ BDK_CSR_MODIFY(c, node, BDK_PEMX_CFG(1),
+ c.cn81xx.lanes4 = (mode == BDK_QLM_MODE_PCIE_1X4);
+ //c.cn81xx.hostmd = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+ c.cn81xx.md = cfg_md);
+ break;
+ case 3: /* Either PEM1 x4 or PEM2 x2 */
+ if (mode == BDK_QLM_MODE_PCIE_1X4)
+ {
+ /* Last 2 lanes of PEM1 */
+ /* PEMX_CFG already setup */
+ }
+ else
+ {
+ /* Two lanes for PEM2 */
+ BDK_CSR_MODIFY(c, node, BDK_RST_SOFT_PRSTX(2),
+ c.s.soft_prst = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT));
+ __bdk_qlm_setup_pem_reset(node, 2, flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+ BDK_CSR_MODIFY(c, node, BDK_PEMX_CFG(2),
+ c.cn81xx.lanes4 = 0;
+ //c.cn81xx.hostmd = !(flags & BDK_QLM_MODE_FLAG_ENDPOINT);
+ c.cn81xx.md = cfg_md);
+ }
+ break;
+ default:
+ return -1;
+ }
+ break;
+ }
+ case BDK_QLM_MODE_SGMII_4X1:
+ case BDK_QLM_MODE_SGMII_2X1:
+ case BDK_QLM_MODE_SGMII_1X1:
+ /* Disable port BGX ports 2-3 on CN80XX */
+ if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+ {
+ BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 2), 0);
+ BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 3), 0);
+ }
+ lmac_type = BDK_BGX_LMAC_TYPES_E_SGMII; /* SGMII */
+ is_bgx = 1;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("SGMII", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ break;
+ case BDK_QLM_MODE_XAUI_1X4:
+ lmac_type = BDK_BGX_LMAC_TYPES_E_XAUI; /* XAUI */
+ is_bgx = 5;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("XAUI", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ break;
+ case BDK_QLM_MODE_RXAUI_2X2:
+ case BDK_QLM_MODE_RXAUI_1X2:
+ lmac_type = BDK_BGX_LMAC_TYPES_E_RXAUI; /* RXAUI */
+ is_bgx = 3;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("RXAUI", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ break;
+ case BDK_QLM_MODE_XFI_4X1:
+ case BDK_QLM_MODE_XFI_2X1:
+ case BDK_QLM_MODE_XFI_1X1:
+ /* Disable port BGX ports 2-3 on CN80XX */
+ if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+ {
+ BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 2), 0);
+ BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 3), 0);
+ }
+ lmac_type = BDK_BGX_LMAC_TYPES_E_TENG_R; /* 10G_R */
+ is_bgx = 1;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("XFI", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ break;
+ case BDK_QLM_MODE_XLAUI_1X4:
+ lmac_type = BDK_BGX_LMAC_TYPES_E_FORTYG_R; /* 40G_R */
+ is_bgx = 5;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("XLAUI", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ break;
+ case BDK_QLM_MODE_10G_KR_4X1:
+ case BDK_QLM_MODE_10G_KR_2X1:
+ case BDK_QLM_MODE_10G_KR_1X1:
+ /* Disable port BGX ports 2-3 on CN80XX */
+ if ((qlm < 2) && cavium_is_altpkg(CAVIUM_CN81XX))
+ {
+ BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 2), 0);
+ BDK_CSR_WRITE(node, BDK_BGXX_CMRX_RX_DMAC_CTL(0, 3), 0);
+ }
+ lmac_type = BDK_BGX_LMAC_TYPES_E_TENG_R; /* 10G_R */
+ is_bgx = 1;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("10G-KR", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ kr_mode = 1;
+ break;
+ case BDK_QLM_MODE_40G_KR4_1X4:
+ lmac_type = BDK_BGX_LMAC_TYPES_E_FORTYG_R; /* 40G_R */
+ is_bgx = 5;
+ lane_mode = __bdk_qlm_get_lane_mode_for_speed_and_ref_clk("40G-KR", qlm, ref_clk, baud_mhz);
+ if (lane_mode == -1)
+ return -1;
+ kr_mode = 1;
+ break;
+ case BDK_QLM_MODE_QSGMII_4X1:
+ lmac_type = BDK_BGX_LMAC_TYPES_E_QSGMII; /* QSGMII */
+ is_bgx = 1;
+ lane_mode = BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII;
+ break;
+ case BDK_QLM_MODE_SATA_2X1:
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_MODE(qlm), c.s.lmode = BDK_GSER_LMODE_E_R_8G_REFCLK100);
+ /* SATA initialization is different than BGX. Call its init function
+ and skip the rest of this routine */
+ return qlm_set_sata(node, qlm, mode, baud_mhz, flags);
+ case BDK_QLM_MODE_DISABLED:
+ /* Set gser for the interface mode */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_CFG(qlm),
+ c.u = 0);
+ /* Put the PHY in reset */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_reset = 1);
+ return 0;
+ default:
+ bdk_error("Unsupported QLM mode %d\n", mode);
+ return -1;
+ }
+
+ BDK_TRACE(QLM, "N%u.QLM%u: Power up...\n", node, qlm);
+
+ /* Power up phy, but keep it in reset */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_pd = 0;
+ c.s.phy_reset = 1);
+
+ /* Set gser for the interface mode */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_CFG(qlm),
+ c.s.sata = is_sata;
+ c.s.ila = is_ilk;
+ c.s.bgx = is_bgx & 1;
+ c.s.bgx_quad = (is_bgx >> 2) & 1;
+ c.s.bgx_dual = (is_bgx >> 1) & 1;
+ c.s.pcie = is_pcie);
+
+ /* Lane mode */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_MODE(qlm),
+ c.s.lmode = lane_mode);
+
+ /* LMAC type. We only program one port as the full setup is done in BGX */
+ if (lmac_type != -1)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_BGXX_CMRX_CONFIG(bgx_block, bgx_index),
+ c.s.enable = 0;
+ c.s.lmac_type = lmac_type);
+ }
+
+ BDK_TRACE(QLM, "N%u.QLM%u: Deassert reset...\n", node, qlm);
+
+ /* Bring phy out of reset */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_reset = 0);
+
+ /* Wait 1us until the management interface is ready to accept
+ read/write commands.*/
+ bdk_wait_usec(1);
+
+ /* Configure the gser pll */
+ __bdk_qlm_init_mode_table(node, qlm, ref_clk);
+
+ /* Remember which lanes are using KR over BGX */
+ if (is_bgx)
+ {
+ int num_lanes = bdk_qlm_get_lanes(node, qlm);
+ for (int lane = 0; lane < num_lanes; lane++)
+ __bdk_qlm_set_lane_kr(node, qlm, lane, kr_mode);
+ }
+
+ /* Wait for reset to complete and the PLL to lock */
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_PLL_STAT(qlm), pll_lock, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_PLL_STAT[pll_lock]\n", qlm);
+ return -1;
+ }
+
+ /* PCIe mode doesn't become ready until the PEM block attempts to bring
+ the interface up. Skip this check for PCIe */
+ if (!is_pcie && BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_QLM_STAT(qlm), rst_rdy, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_QLM_STAT[rst_rdy]\n", qlm);
+ return -1;
+ }
+
+ /* cdrlock will be checked in the BGX */
+
+ /* Errata (GSER-27140) SERDES temperature drift sensitivity in receiver */
+ int channel_loss = bdk_config_get_int(BDK_CONFIG_QLM_CHANNEL_LOSS, node, qlm);
+ __bdk_qlm_errata_gser_27140(node, qlm, baud_mhz, channel_loss);
+
+ /* Apply any custom tuning */
+ __bdk_qlm_tune(node, qlm, mode, baud_mhz);
+
+ /* Some modes require 4 lanes, which spans DLMs. For these modes, we need
+ to setup the second DLM at the same time we setup the first. The second
+ DLM also must use the same reference clock as the first */
+ bool paired_dlm = ((qlm & 1) == 0) && /* We're on the first (even) DLM */
+ ((mode == BDK_QLM_MODE_PCIE_1X4) || /* We're using a 4 lane mode */
+ (mode == BDK_QLM_MODE_XAUI_1X4) ||
+ (mode == BDK_QLM_MODE_XLAUI_1X4) ||
+ (mode == BDK_QLM_MODE_40G_KR4_1X4));
+ if (paired_dlm)
+ {
+ /* Use the same reference clock for the second QLM */
+ BDK_CSR_WRITE(node, BDK_GSERX_REFCLK_SEL(qlm + 1),
+ BDK_CSR_READ(node, BDK_GSERX_REFCLK_SEL(qlm)));
+ return bdk_qlm_set_mode(node, qlm + 1, mode, baud_mhz, flags);
+ }
+
+ return 0;
+}
+
+/**
+ * Get the speed (Gbaud) of the QLM in Mhz.
+ *
+ * @param qlm QLM to examine
+ *
+ * @return Speed in Mhz
+ */
+int bdk_qlm_get_gbaud_mhz(bdk_node_t node, int qlm)
+{
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+ if (gserx_cfg.u == 0)
+ return 0;
+ if (gserx_cfg.s.pcie)
+ {
+ /* QLMs in PCIe mode ignore LMODE and get their speed from
+ the PEM block that controls them */
+ int pem;
+ switch (qlm)
+ {
+ case 0: /* PEM0 */
+ case 1: /* PEM0 */
+ pem = 0;
+ break;
+ case 2: /* PEM1 */
+ pem = 1;
+ break;
+ case 3: /* PEM1 or PEM2 */
+ {
+ BDK_CSR_INIT(pemx_cfg, node, BDK_PEMX_CFG(1));
+ if (pemx_cfg.cn81xx.lanes4)
+ pem = 1;
+ else
+ pem = 2;
+ break;
+ }
+ default:
+ bdk_fatal("QLM%d: In PCIe mode, which shouldn't happen\n", qlm);
+ }
+ return __bdk_qlm_get_gbaud_mhz_pem(node, pem);
+ }
+ else if (gserx_cfg.s.sata)
+ {
+ int sata;
+ switch (qlm)
+ {
+ case 3:
+ sata = 0;
+ break;
+ default:
+ return 0;
+ }
+ BDK_CSR_INIT(sata_uctl_ctl, node, BDK_SATAX_UCTL_CTL(sata));
+ if (!sata_uctl_ctl.s.a_clk_en)
+ return 0;
+ BDK_CSR_INIT(sctl, node, BDK_SATAX_UAHC_P0_SCTL(sata));
+ switch (sctl.s.spd)
+ {
+ case 1: return 1500;
+ case 2: return 3000;
+ case 3: return 6000;
+ default: return 6000; /* No limit, assume 6G */
+ }
+ }
+ else
+ return __bdk_qlm_get_gbaud_mhz_lmode(node, qlm);
+}
+
+/**
+ * Initialize the QLM layer
+ */
+void bdk_qlm_init(bdk_node_t node)
+{
+ /* Setup how each PEM drives the PERST lines */
+ for (int pem = 0; pem < 3; pem++)
+ {
+ BDK_CSR_INIT(rst_ctlx, node, BDK_RST_CTLX(pem));
+ __bdk_qlm_setup_pem_reset(node, pem, !rst_ctlx.s.host_mode);
+ }
+}
+
+static void __bdk_qlm_sff81xx_set_reference(bdk_node_t node, int qlm, int ref_clk)
+{
+ int use_clock;
+
+ if (CAVIUM_IS_MODEL(CAVIUM_CN88XX) || CAVIUM_IS_MODEL(CAVIUM_CN83XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+ {
+ // Common clock 0 is 156MHz
+ // Common clock 1 is 100MHz
+ switch (qlm)
+ {
+ case 0:
+ use_clock = 1; /* DLMC_REF_CLK1 of 100MHz */
+ break;
+ case 1:
+ if (ref_clk == REF_100MHZ)
+ use_clock = 1; /* DLMC_REF_CLK1 of 100MHz */
+ else
+ use_clock = 2; /* DLM1_REF_CLK of 156MHz */
+ break;
+ case 2:
+ case 3:
+ default:
+ if (ref_clk == REF_100MHZ)
+ use_clock = 1; /* DLMC_REF_CLK1 of 100MHz */
+ else
+ use_clock = 2; /* DLM1_REF_CLK of 156MHz */
+ break;
+ }
+
+ BDK_TRACE(QLM, "Setting N%d.QLM%d to use ref clock %d\n", node, qlm, use_clock);
+ }
+ else
+ {
+ bdk_error("Update %s for qlm auto config of this chip\n",__FUNCTION__);
+ return;
+ }
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+ c.s.com_clk_sel = (use_clock != 2);
+ c.s.use_com1 = (use_clock == 1));
+}
+
+int bdk_qlm_auto_config(bdk_node_t node)
+{
+ return -1;
+}
+
+/**
+ * For Cavium SFF board, query the DIP switches in GPIO o determine the QLM setup.
+ * Configure the GPIOs to read the DLM settings
+ * SW1.1 -> DLM0_SEL -> GPIO_26
+ * SW1.2 -> DLM1_SEL -> GPIO_25
+ * SW1.3 -> DLM2_SEL -> GPIO_31
+ * SW1.4 -> DLM3_SEL -> GPIO_4
+ *V1.x boards SW3.8 -> QSGMII/XFI SEL ->GPIO_9
+ *V2.x boards SW3.7 -> QSGMII/XFI SEL ->GPIO_36
+*/
+int bdk_qlm_dip_auto_config(bdk_node_t node)
+{
+ bdk_qlm_modes_t dlm_mode[4];
+ int dlm_speed = 0;
+ int use_ref = 0;
+ bdk_qlm_mode_flags_t dlm_flags = 0;
+
+ unsigned int dlm_config, dlm3, dlm2, dlm1, dlm0;
+ uint64_t gpio = 0;
+
+ /* Configure the GPIOs to read the DLM settings */
+ /* SW1.1 -> DLM0_SEL -> GPIO_26 */
+ /* SW1.2 -> DLM1_SEL -> GPIO_25 */
+ /* SW1.3 -> DLM2_SEL -> GPIO_31 */
+ /* SW1.4 -> DLM3_SEL -> GPIO_4 */
+ //V1.x boards /* SW3.8 -> QSGMII/XFI SEL ->GPIO_9 */
+ //V2.x boards /* SW3.7 -> QSGMII/XFI SEL ->GPIO_36 */
+ /* Configure the GPIOs are input */
+ bdk_gpio_initialize(node, 26, 0, 0);
+ bdk_gpio_initialize(node, 25, 0, 0);
+ bdk_gpio_initialize(node, 31, 0, 0);
+ bdk_gpio_initialize(node, 4, 0, 0);
+ bdk_gpio_initialize(node, 36, 0, 0);
+
+
+ /* Read the GPIOs */
+ gpio = bdk_gpio_read(node, 0);
+
+ dlm3 = !!(gpio & (1ULL<<4));
+ dlm2 = !!(gpio & (1ULL<<31));
+ dlm1 = !!(gpio & (1ULL<<25));
+ dlm0 = !!(gpio & (1ULL<<26));
+
+
+ dlm_config = (dlm0<<3)| (dlm1<<2) | (dlm2<<1) | (dlm3);
+
+ BDK_TRACE(QLM, "DLM CONFIG:%d gpio36: %d\n", dlm_config, !!(gpio & (1ULL<<36)));
+
+ switch(dlm_config)
+ {
+ case 0:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 1:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 2:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 3:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = BDK_QLM_MODE_DISABLED;
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 4:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 5:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 6:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 7:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 8:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 9:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 10:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 11:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[1] = BDK_QLM_MODE_PCIE_1X4;
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 12:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 13:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ case 14:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_PCIE_1X2;
+ break;
+ case 15:
+ dlm_mode[0] = BDK_QLM_MODE_PCIE_1X2;
+ dlm_mode[1] = (!!(gpio & (1ULL<<36)) ? BDK_QLM_MODE_XFI_1X1 : BDK_QLM_MODE_QSGMII_4X1);
+ dlm_mode[2] = BDK_QLM_MODE_XFI_2X1;
+ dlm_mode[3] = BDK_QLM_MODE_SATA_2X1;
+ break;
+ default:
+ return -1;
+ }
+
+ for(int dlm = 0; dlm < 4; dlm++)
+ {
+ const char *dlm_mode_str = bdk_qlm_mode_tostring(dlm_mode[dlm]);
+ switch(dlm_mode[dlm])
+ {
+ case BDK_QLM_MODE_DISABLED:
+ break;
+ case BDK_QLM_MODE_XFI_2X1:
+ case BDK_QLM_MODE_XFI_1X1:
+ use_ref = REF_156MHZ;
+ dlm_speed = 10312;
+ break;
+ case BDK_QLM_MODE_SATA_2X1:
+ dlm_speed = 6000;
+ use_ref = REF_100MHZ;
+ break;
+ case BDK_QLM_MODE_PCIE_1X2:
+ case BDK_QLM_MODE_PCIE_1X4:
+ dlm_speed = 8000;
+ use_ref =REF_100MHZ;
+ break;
+ case BDK_QLM_MODE_QSGMII_4X1:
+ use_ref = REF_100MHZ;
+ dlm_speed = 5000;
+ break;
+ default:
+ bdk_error("Unsupported N%d.QLM%d mode: %s(%d)",
+ node, dlm,
+ dlm_mode_str ? dlm_mode_str : "???",
+ dlm_mode[dlm]);
+ return -1;
+ }
+ if ((1 == dlm) && (dlm_mode[dlm] != BDK_QLM_MODE_QSGMII_4X1) && (dlm_mode[dlm] != BDK_QLM_MODE_DISABLED))
+ {
+ /* This code is specific to sff8104 board
+ ** QSGMII phy is wired to dlm1-gser lane 2
+ ** AQR-107 phy is wired to dlm1-gser lane 3
+ ** bdk always uses bgx0.port0 on that board
+ */
+ // If dlm1 is in XFI mode, change PHY address to mdio of aquantia phy
+ unsigned mdio_bus = 1;
+ unsigned mdio_addr = 0;
+ int phy_cfg = 0xff<<24 | ((mdio_bus& 0xf)<<8) | (mdio_addr & 0xff);
+ bdk_config_set_int((uint32_t) phy_cfg,BDK_CONFIG_PHY_ADDRESS, node, 0, 0);
+ /* Indicate serdes lane 3 , aquantia phy active */
+ int aq_phy = (0x3<<8) | 1;
+ bdk_config_set_int(aq_phy, BDK_CONFIG_AQUANTIA_PHY,node,0,0);
+ BDK_TRACE(QLM,"Disabling phys 0.1,0.2,0.3\n");
+ for (int i = 1; i<4; i++) {
+ bdk_config_set_int(-1,BDK_CONFIG_PHY_ADDRESS, node, 0, i);
+ bdk_config_set_int(0,BDK_CONFIG_BGX_ENABLE,node,0,i);
+ }
+ }
+
+ BDK_TRACE(QLM, "Setting N%d.QLM%d mode %s(%d), speed %d, flags 0x%x\n",
+ node, dlm, dlm_mode_str, dlm_mode[dlm], dlm_speed, dlm_flags);
+
+ /* Set the reference clock for this QLM */
+ __bdk_qlm_sff81xx_set_reference(node, dlm, use_ref);
+
+ if (bdk_qlm_set_mode(node, dlm, dlm_mode[dlm], dlm_speed, dlm_flags))
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common-sata.c b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common-sata.c
new file mode 100644
index 0000000000..9e31ad1dce
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common-sata.c
@@ -0,0 +1,625 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 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**************************************/
+#include <bdk.h>
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/qlm/bdk-qlm-common.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-sata.h"
+
+/**
+ * Initialize a DLM/QLM for use with SATA controllers
+ *
+ * @param node Node to intialize
+ * @param qlm Which DLM/QLM to init
+ * @param baud_mhz QLM speed in Gbaud
+ * @param sata_first First SATA controller connected to this DLM/QLM
+ * @param sata_last Last SATA controller connected to this DLM/QLM (inclusive)
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_set_sata_cn8xxx(bdk_node_t node, int qlm, int baud_mhz, int sata_first, int sata_last)
+{
+ const int NUM_LANES = sata_last - sata_first + 1;
+ const int MAX_A_CLK = 333000000; /* Max of 333Mhz */
+
+ /* 26.4.1 Cold Reset */
+ /* 1. Ensure that the SerDes reference clock is up and stable. */
+ /* Already done */
+
+ /* 2. Optionally program the GPIO CSRs for SATA features.
+ a. For cold-presence detect, select a GPIO for the input and program GPI-
+ O_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E::SATA(0..15)_CP_DET.
+ b. For mechanical-presence detect, select a GPIO for the input and program
+ GPIO_BIT_CFG(0..50)[PIN_SEL] = GPI-
+ O_PIN_SEL_E::SATA(0..15)_MP_SWITCH.
+ c. For BIST board-test loopback, select a GPIO for the input and program GPI-
+ O_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E:::SATA_LAB_LB.
+ d. For LED activity, select a GPIO for the output and program GPI-
+ O_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E:::SATA(0..15)_ACT_LED.
+ e. For cold-presence power-on-device, select a GPIO for the output and program
+ GPIO_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E:::SATA(0..15)_CP_-
+ POD. */
+ /* Skipping */
+
+ /* 3. Optionally program the SGPIO unit. */
+ /* Skipping */
+
+ /* 4. Assert all resets:
+ a. UAHC reset: SATA(0..15)_UCTL_CTL[SATA_UAHC_RST] = 1
+ b. UCTL reset: SATA(0..15)_UCTL_CTL[SATA_UCTL_RST] = 1 */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.sata_uahc_rst = 1;
+ c.s.sata_uctl_rst = 1);
+ }
+
+ /* 5. Configure the ACLK:
+ a. Reset the clock dividers: SATA(0..15)_UCTL_CTL[A_CLKDIV_RST] = 1.
+ b. Select the ACLK frequency (refer to maximum values in Table 26 1).
+ i. SATA(0..15)_UCTL_CTL[A_CLKDIV_SEL] = desired value,
+ ii. SATA(0..15)_UCTL_CTL[A_CLK_EN] = 1 to enable the ACLK.
+ c. Deassert the ACLK clock divider reset:
+ SATA(0..15)_UCTL_CTL[A_CLKDIV_RST] = 0. */
+ int divisor = (bdk_clock_get_rate(node, BDK_CLOCK_SCLK) + MAX_A_CLK - 1) / MAX_A_CLK;
+ int a_clkdiv;
+ /* This screwy if logic is from the description of
+ SATAX_UCTL_CTL[a_clkdiv_sel] in the CSR */
+ if (divisor <= 4)
+ {
+ a_clkdiv = divisor - 1;
+ /* Divisor matches calculated value */
+ }
+ else if (divisor <= 6)
+ {
+ a_clkdiv = 4;
+ divisor = 6;
+ }
+ else if (divisor <= 8)
+ {
+ a_clkdiv = 5;
+ divisor = 8;
+ }
+ else if (divisor <= 16)
+ {
+ a_clkdiv = 6;
+ divisor = 16;
+ }
+ else if (divisor <= 24)
+ {
+ a_clkdiv = 7;
+ divisor = 24;
+ }
+ else
+ {
+ bdk_error("Unable to determine SATA clock divisor\n");
+ return -1;
+ }
+ /* Calculate the final clock rate */
+ int a_clk = bdk_clock_get_rate(node, BDK_CLOCK_SCLK) / divisor;
+
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.a_clkdiv_rst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.a_clk_byp_sel = 0;
+ c.s.a_clkdiv_sel = a_clkdiv;
+ c.s.a_clk_en = 1);
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.a_clkdiv_rst = 0);
+ }
+ bdk_wait_usec(1);
+
+ /* 8. Configure PHY for SATA. Refer to Section 21.1.2. */
+ /* Done below, section 24.1.2.3 */
+
+ /* 9. TBD: Poll QLM2_MPLL_STATUS for MPLL lock */
+ /* Not needed */
+
+ /* 10. Initialize UAHC as described in the AHCI specification
+ (UAHC_* registers). */
+ /* Done when a SATA driver is initialized */
+
+ /* 24.1.2.3 SATA Configuration
+ Software must perform the following steps to configure the GSER_WEST
+ for a SATA application. Note that the GSERW steps are on a QLM basis. */
+
+ /* 1. Configure the SATA controller (refer to Chapter 26). */
+ /* This is the code above */
+
+ /* 2. Configure the QLM Reference clock.
+ Set GSER(0..13)_REFCLK_SEL[COM_CLK_SEL] = 1 to source the reference
+ clock from the external clock multiplexer.
+ Configure GSER(0..13)_REFCLK_SEL[USE_COM1]:
+ 0 = use QLMC_REF_CLK0_P/N
+ 1 = use QLMC_REF_CLK1_P/N */
+ /* Already done */
+
+ /* Make sure the PHY is in reset before we reconfig */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_reset = 1);
+ bdk_wait_usec(1);
+
+ /* 3. Configure the QLM for SATA mode: set GSER(0..13)_CFG[SATA] = 1. */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_CFG(qlm),
+ c.u = 0;
+ c.s.sata = 1);
+
+ /* 9. Clear the appropriate lane resets:
+ GSER(0..13)_SATA_LANE_RST[Ln_RST] = 0, where n is the lane number 0-3. */
+ BDK_CSR_WRITE(node, BDK_GSERX_SATA_LANE_RST(qlm), 0);
+ BDK_CSR_READ(node, BDK_GSERX_SATA_LANE_RST(qlm));
+
+ /* We'll check for the SATA_PCS Ready in step 8a below */
+ /* Short 1 usec wait */
+ bdk_wait_usec(1);
+
+ /* 4. Take the PHY out of reset: write GSER(0..13)_PHY_CTL[PHY_RESET] = 0. */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_reset = 0);
+
+ /* 4a. Poll for PHY RST_RDY indicating the PHY has initialized before
+ trying to access internal registers to reconfigure for SATA */
+ /* 8. Wait for GSER(0..13)_QLM_STAT[RST_RDY] = 1, indicating that the PHY
+ has been reconfigured and PLLs are locked. */
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_QLM_STAT(qlm), rst_rdy, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_QLM_STAT[rst_rdy]\n", qlm);
+ return -1;
+ }
+
+ /* Workaround for errata GSER-30310: SATA HDD Not Ready due to
+ PHY SDLL/LDLL lockup at 3GHz */
+ for (int slice = 0; slice < NUM_LANES / 2; slice++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_SLICEX_PCIE1_MODE(qlm, slice),
+ c.s.rx_pi_bwsel = 1;
+ c.s.rx_ldll_bwsel = 1;
+ c.s.rx_sdll_bwsel = 1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_SLICEX_PCIE2_MODE(qlm, slice),
+ c.s.rx_pi_bwsel = 1;
+ c.s.rx_ldll_bwsel = 1;
+ c.s.rx_sdll_bwsel = 1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_SLICEX_PCIE3_MODE(qlm, slice),
+ c.s.rx_pi_bwsel = 1;
+ c.s.rx_ldll_bwsel = 1;
+ c.s.rx_sdll_bwsel = 1);
+ }
+
+ /* 5. Change the P2 termination
+ GSERn_RX_PWR_CTRL_P2[P2_RX_SUBBLK_PD<0>] = 0 (termination) */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_RX_PWR_CTRL_P2(qlm),
+ c.s.p2_rx_subblk_pd &= 0x1e);
+
+ /* 6. Modify the electrical IDLE detect on delay: set
+ GSER(0..13)_LANE(0..3)_MISC_CFG_0[EIE_DET_STL_ON_TIME] = 0x4 */
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_MISC_CFG_0(qlm, lane),
+ c.s.eie_det_stl_on_time = 4);
+ }
+
+ /* 7. Modify the PLL and lane-protocol-mode registers to configure the
+ PHY for SATA */
+ /* Errata (GSER-26724) SATA never indicates GSER QLM_STAT[RST_RDY]
+ We program PLL_PX_MODE_0 last due to this errata */
+ for (int p=0; p<3; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PLL_PX_MODE_1(qlm, p),
+ c.s.pll_16p5en = 0x0;
+ c.s.pll_cpadj = 0x2;
+ c.s.pll_pcie3en = 0;
+ c.s.pll_opr = 0x0;
+ c.s.pll_div = 0x1e);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_PX_MODE_0(qlm, p),
+ c.s.ctle = 0x0;
+ c.s.pcie = 0;
+ c.s.tx_ldiv = 0x0;
+ c.s.rx_ldiv = 2 - p;
+ c.s.srate = 0;
+ c.s.tx_mode = 3;
+ c.s.rx_mode = 3);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_PX_MODE_1(qlm, p),
+ c.s.vma_fine_cfg_sel = 0;
+ c.s.vma_mm = 1;
+ c.s.cdr_fgain = 0xa;
+ c.s.ph_acc_adj = 0x15);
+ }
+ for (int p=0; p<3; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PLL_PX_MODE_0(qlm, p),
+ c.s.pll_icp = 0x1;
+ c.s.pll_rloop = 0x3;
+ c.s.pll_pcs_div = 0x5);
+ }
+
+ for (int s = 0; s < NUM_LANES / 2; s++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_SLICEX_RX_SDLL_CTRL(qlm, s),
+ c.s.pcs_sds_oob_clk_ctrl = 2;
+ c.s.pcs_sds_rx_sdll_tune = 0;
+ c.s.pcs_sds_rx_sdll_swsel = 0);
+ }
+
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_MISC_CFG_0(qlm, lane),
+ c.s.use_pma_polarity = 0;
+ c.s.cfg_pcs_loopback = 0;
+ c.s.pcs_tx_mode_ovrrd_en = 0;
+ c.s.pcs_rx_mode_ovrrd_en = 0;
+ c.s.cfg_eie_det_cnt = 0;
+ c.s.eie_det_stl_on_time = 4;
+ c.s.eie_det_stl_off_time = 0;
+ c.s.tx_bit_order = 1;
+ c.s.rx_bit_order = 1);
+ }
+
+ /* 8. Wait for GSER(0..13)_QLM_STAT[RST_RDY] = 1, indicating that the PHY
+ has been reconfigured and PLLs are locked. */
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_QLM_STAT(qlm), rst_rdy, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_QLM_STAT[rst_rdy]\n", qlm);
+ return -1;
+ }
+ /* 8a. Check that the SATA_PCS is "Ready" here, should be but check it */
+ /* Poll GSERX_SATA_STATUS for PX_RDY = 1 */
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_SATA_STATUS(qlm), p0_rdy, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_SATA_STATUS[p0_rdy]\n", qlm);
+ return -1;
+ }
+
+ /* Add 1ms delay for everything to stabilize*/
+ bdk_wait_usec(1000);
+
+ /* Apply any custom tuning */
+ __bdk_qlm_tune(node, qlm, BDK_QLM_MODE_SATA_4X1, baud_mhz);
+ bdk_wait_usec(1000);
+
+
+ /* 6. Deassert UCTL and UAHC resets:
+ a. SATA(0..15)_UCTL_CTL[SATA_UAHC_RST] = 0
+ b. SATA(0..15)_UCTL_CTL[SATA_UCTL_RST] = 0
+ c. Wait 10 ACLK cycles before accessing any ACLK-only registers. */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.sata_uahc_rst = 0;
+ c.s.sata_uctl_rst = 0);
+ }
+ bdk_wait_usec(1);
+
+ /* 7. Enable conditional SCLK of UCTL by writing
+ SATA(0..15)_UCTL_CTL[CSCLK_EN] = 1. */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+ {
+ /* CN9XXX make coprocessor clock automatic */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.cn8.csclk_en = 1);
+ }
+ }
+
+ /* Check BIST on the SATA controller. Start BIST in parallel on the
+ controllers */
+
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ /* Make sure BIST is configured properly before we start it. We
+ want full BIST, not just CLEAR */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.clear_bist = 0;
+ c.s.start_bist = 0);
+ /* Start BIST */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.start_bist = 1);
+ }
+ bdk_wait_usec(1000);
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_INIT(bist, node, BDK_SATAX_UCTL_BIST_STATUS(p));
+ if (bist.u)
+ bdk_error("N%d.SATA%d: Controller failed BIST (0x%llx)\n", node, p, bist.u);
+ else
+ BDK_TRACE(SATA, "N%d.SATA%d: Passed BIST\n", node, p);
+ }
+ /* Clear start_bist so it is ready for the next run */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.start_bist = 0);
+ }
+
+ int spd;
+ if (baud_mhz < 3000)
+ spd = 1;
+ else if (baud_mhz < 6000)
+ spd = 2;
+ else
+ spd = 3;
+
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ /* From the synopsis data book, SATAX_UAHC_GBL_TIMER1MS is the
+ AMBA clock in MHz * 1000, which is a_clk(Hz) / 1000 */
+ BDK_TRACE(QLM, "QLM%d: SATA%d set to %d Hz\n", qlm, p, a_clk);
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_TIMER1MS(p),
+ c.s.timv = a_clk / 1000);
+ /* Set speed */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_SCTL(p),
+ c.s.ipm = 3; /* Disable parial and slumber power management */
+ c.s.spd = spd);
+ /* The following SATA setup is from the AHCI 1.3 spec, section
+ 10.1.1, Firmware Specific Initialization. */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_CAP(p),
+ c.s.sss = 1; /* Support staggered spin-up */
+ c.s.smps = 1); /* Support mechanical presence switch */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_PI(p),
+ c.s.pi = 1); /* One port per controller */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_CMD(p),
+ c.s.hpcp = 1; /* Hot-plug-capable support */
+ c.s.mpsp = 1; /* Mechanical presence switch attached to port */
+ c.s.cpd = 1); /* Cold-presence detection */
+ }
+ return 0;
+}
+
+/**
+ * Initialize a DLM/QLM for use with SATA controllers
+ *
+ * @param node Node to intialize
+ * @param qlm Which DLM/QLM to init
+ * @param baud_mhz QLM speed in Gbaud
+ * @param sata_first First SATA controller connected to this DLM/QLM
+ * @param sata_last Last SATA controller connected to this DLM/QLM (inclusive)
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_set_sata_cn9xxx(bdk_node_t node, int qlm, int baud_mhz, int sata_first, int sata_last)
+{
+ //const int NUM_LANES = sata_last - sata_first + 1;
+ const int MAX_A_CLK = 333000000; /* Max of 333Mhz */
+
+ /* 26.4.1 Cold Reset */
+ /* 1. Ensure that the SerDes reference clock is up and stable. */
+ /* Already done */
+
+ /* 2. Optionally program the GPIO CSRs for SATA features.
+ a. For cold-presence detect, select a GPIO for the input and program GPI-
+ O_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E::SATA(0..15)_CP_DET.
+ b. For mechanical-presence detect, select a GPIO for the input and program
+ GPIO_BIT_CFG(0..50)[PIN_SEL] = GPI-
+ O_PIN_SEL_E::SATA(0..15)_MP_SWITCH.
+ c. For BIST board-test loopback, select a GPIO for the input and program GPI-
+ O_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E:::SATA_LAB_LB.
+ d. For LED activity, select a GPIO for the output and program GPI-
+ O_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E:::SATA(0..15)_ACT_LED.
+ e. For cold-presence power-on-device, select a GPIO for the output and program
+ GPIO_BIT_CFG(0..50)[PIN_SEL] = GPIO_PIN_SEL_E:::SATA(0..15)_CP_-
+ POD. */
+ /* Skipping */
+
+ /* 3. Optionally program the SGPIO unit. */
+ /* Skipping */
+
+ /* 4. Assert all resets:
+ a. UAHC reset: SATA(0..15)_UCTL_CTL[SATA_UAHC_RST] = 1
+ b. UCTL reset: SATA(0..15)_UCTL_CTL[SATA_UCTL_RST] = 1 */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.sata_uahc_rst = 1;
+ c.s.sata_uctl_rst = 1);
+ }
+
+ /* 5. Configure the ACLK:
+ a. Reset the clock dividers: SATA(0..15)_UCTL_CTL[A_CLKDIV_RST] = 1.
+ b. Select the ACLK frequency (refer to maximum values in Table 26 1).
+ i. SATA(0..15)_UCTL_CTL[A_CLKDIV_SEL] = desired value,
+ ii. SATA(0..15)_UCTL_CTL[A_CLK_EN] = 1 to enable the ACLK.
+ c. Deassert the ACLK clock divider reset:
+ SATA(0..15)_UCTL_CTL[A_CLKDIV_RST] = 0. */
+ int divisor = (bdk_clock_get_rate(node, BDK_CLOCK_SCLK) + MAX_A_CLK - 1) / MAX_A_CLK;
+ int a_clkdiv;
+ /* This screwy if logic is from the description of
+ SATAX_UCTL_CTL[a_clkdiv_sel] in the CSR */
+ if (divisor <= 4)
+ {
+ a_clkdiv = divisor - 1;
+ /* Divisor matches calculated value */
+ }
+ else if (divisor <= 6)
+ {
+ a_clkdiv = 4;
+ divisor = 6;
+ }
+ else if (divisor <= 8)
+ {
+ a_clkdiv = 5;
+ divisor = 8;
+ }
+ else if (divisor <= 16)
+ {
+ a_clkdiv = 6;
+ divisor = 16;
+ }
+ else if (divisor <= 24)
+ {
+ a_clkdiv = 7;
+ divisor = 24;
+ }
+ else
+ {
+ bdk_error("Unable to determine SATA clock divisor\n");
+ return -1;
+ }
+ /* Calculate the final clock rate */
+ int a_clk = bdk_clock_get_rate(node, BDK_CLOCK_SCLK) / divisor;
+
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.a_clkdiv_rst = 1);
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.a_clk_byp_sel = 0;
+ c.s.a_clkdiv_sel = a_clkdiv;
+ c.s.a_clk_en = 1);
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.a_clkdiv_rst = 0);
+ }
+ bdk_wait_usec(1);
+
+ /* 8. Configure PHY for SATA. Refer to Section 21.1.2. */
+ /* Done below, section 24.1.2.3 */
+
+ /* 9. TBD: Poll QLM2_MPLL_STATUS for MPLL lock */
+ /* Not needed */
+
+ /* 10. Initialize UAHC as described in the AHCI specification
+ (UAHC_* registers). */
+ /* Done when a SATA driver is initialized */
+
+ /* 24.1.2.3 SATA Configuration
+ Software must perform the following steps to configure the GSER_WEST
+ for a SATA application. Note that the GSERW steps are on a QLM basis. */
+
+ /* 1. Configure the SATA controller (refer to Chapter 26). */
+ /* This is the code above */
+
+ /* 2. Configure the QLM Reference clock.
+ Set GSER(0..13)_REFCLK_SEL[COM_CLK_SEL] = 1 to source the reference
+ clock from the external clock multiplexer.
+ Configure GSER(0..13)_REFCLK_SEL[USE_COM1]:
+ 0 = use QLMC_REF_CLK0_P/N
+ 1 = use QLMC_REF_CLK1_P/N */
+ /* Already done */
+
+ // FIXME: GSERN setup
+
+ /* 6. Deassert UCTL and UAHC resets:
+ a. SATA(0..15)_UCTL_CTL[SATA_UAHC_RST] = 0
+ b. SATA(0..15)_UCTL_CTL[SATA_UCTL_RST] = 0
+ c. Wait 10 ACLK cycles before accessing any ACLK-only registers. */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.sata_uahc_rst = 0;
+ c.s.sata_uctl_rst = 0);
+ }
+ bdk_wait_usec(1);
+
+ /* 7. Enable conditional SCLK of UCTL by writing
+ SATA(0..15)_UCTL_CTL[CSCLK_EN] = 1. */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ if (CAVIUM_IS_MODEL(CAVIUM_CN8XXX))
+ {
+ /* CN9XXX make coprocessor clock automatic */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.cn8.csclk_en = 1);
+ }
+ }
+
+ /* Check BIST on the SATA controller. Start BIST in parallel on the
+ controllers */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ /* Make sure BIST is configured properly before we start it. We
+ want full BIST, not just CLEAR */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.clear_bist = 0;
+ c.s.start_bist = 0);
+ /* Start BIST */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.start_bist = 1);
+ }
+ bdk_wait_usec(1000);
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_INIT(bist, node, BDK_SATAX_UCTL_BIST_STATUS(p));
+ if (bist.u)
+ bdk_error("N%d.SATA%d: Controller failed BIST (0x%llx)\n", node, p, bist.u);
+ else
+ BDK_TRACE(SATA, "N%d.SATA%d: Passed BIST\n", node, p);
+ }
+ /* Clear start_bist so it is ready for the next run */
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UCTL_CTL(p),
+ c.s.start_bist = 0);
+ }
+
+ int spd;
+ if (baud_mhz < 3000)
+ spd = 1;
+ else if (baud_mhz < 6000)
+ spd = 2;
+ else
+ spd = 3;
+
+ for (int p = sata_first; p <= sata_last; p++)
+ {
+ /* From the synopsis data book, SATAX_UAHC_GBL_TIMER1MS is the
+ AMBA clock in MHz * 1000, which is a_clk(Hz) / 1000 */
+ BDK_TRACE(QLM, "QLM%d: SATA%d set to %d Hz\n", qlm, p, a_clk);
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_TIMER1MS(p),
+ c.s.timv = a_clk / 1000);
+ /* Set speed */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_SCTL(p),
+ c.s.ipm = 3; /* Disable parial and slumber power management */
+ c.s.spd = spd);
+ /* The following SATA setup is from the AHCI 1.3 spec, section
+ 10.1.1, Firmware Specific Initialization. */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_CAP(p),
+ c.s.sss = 1; /* Support staggered spin-up */
+ c.s.smps = 1); /* Support mechanical presence switch */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_GBL_PI(p),
+ c.s.pi = 1); /* One port per controller */
+ BDK_CSR_MODIFY(c, node, BDK_SATAX_UAHC_P0_CMD(p),
+ c.s.hpcp = 1; /* Hot-plug-capable support */
+ c.s.mpsp = 1; /* Mechanical presence switch attached to port */
+ c.s.cpd = 1); /* Cold-presence detection */
+ }
+ return 0;
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common.c b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common.c
new file mode 100644
index 0000000000..2b3390228a
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-common.c
@@ -0,0 +1,1636 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 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**************************************/
+#include <bdk.h>
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/qlm/bdk-qlm-common.h"
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-pem.h"
+#include "libbdk-hal/bdk-config.h"
+#include "libbdk-hal/bdk-utils.h"
+#include "libbdk-hal/bdk-twsi.h"
+
+/* Indexed by QLM number and lane */
+static uint64_t prbs_errors[14][4];
+
+/**
+ * Figure out which lane mode to use for a given reference clock and GBaud
+ *
+ * @param mode_name String name for error messages
+ * @param qlm QlM being configured
+ * @param ref_clk Reference clock in hertz
+ * @param baud_mhz Baud rate in Mhz
+ *
+ * @return Lane mode or -1 on failure
+ */
+int __bdk_qlm_get_lane_mode_for_speed_and_ref_clk(const char *mode_name, int qlm, int ref_clk, int baud_mhz)
+{
+ if (baud_mhz <= 1250)
+ {
+ if ((ref_clk == REF_156MHZ) || (ref_clk == REF_100MHZ))
+ return BDK_GSER_LMODE_E_R_125G_REFCLK15625_SGMII;
+ else
+ {
+ bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+ }
+ }
+ else if (baud_mhz <= 2500)
+ {
+ if (ref_clk == REF_100MHZ)
+ return BDK_GSER_LMODE_E_R_25G_REFCLK100;
+ else if (ref_clk == REF_125MHZ)
+ return BDK_GSER_LMODE_E_R_25G_REFCLK125;
+ else
+ {
+ bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+ }
+ }
+ else if (baud_mhz <= 3125)
+ {
+ if (ref_clk == REF_156MHZ)
+ return BDK_GSER_LMODE_E_R_3125G_REFCLK15625_XAUI;
+ else
+ {
+ bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+ }
+ }
+ else if (baud_mhz <= 5000)
+ {
+ if (ref_clk == REF_100MHZ)
+ return BDK_GSER_LMODE_E_R_5G_REFCLK100;
+ else if (ref_clk == REF_125MHZ)
+ return BDK_GSER_LMODE_E_R_5G_REFCLK125;
+ else
+ return BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII;
+ }
+ else if (baud_mhz <= 6250)
+ {
+ if (ref_clk == REF_156MHZ)
+ return BDK_GSER_LMODE_E_R_625G_REFCLK15625_RXAUI;
+ else
+ {
+ bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+ }
+ }
+ else if (baud_mhz <= 8000)
+ {
+ if (ref_clk == REF_100MHZ)
+ return BDK_GSER_LMODE_E_R_8G_REFCLK100;
+ else if (ref_clk == REF_125MHZ)
+ return BDK_GSER_LMODE_E_R_8G_REFCLK125;
+ else
+ {
+ bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+ }
+ }
+ else /* Baud 10312.5 */
+ {
+ if (ref_clk == REF_156MHZ)
+ return BDK_GSER_LMODE_E_R_103125G_REFCLK15625_KR;
+ else
+ {
+ bdk_error("Invalid reference clock for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+ }
+ }
+ bdk_error("Invalid speed for %s on QLM%d with speed %d, ref %d Mhz\n", mode_name, qlm, baud_mhz, ref_clk / 1000000);
+ return -1;
+}
+
+/**
+ * Setup the PEM to either driver or receive reset from PRST based on RC or EP
+ *
+ * @param node Node to use in a Numa setup
+ * @param pem Which PEM to setuo
+ * @param is_endpoint
+ * Non zero if PEM is a EP
+ */
+void __bdk_qlm_setup_pem_reset(bdk_node_t node, int pem, int is_endpoint)
+{
+ /* Make sure is_endpoint is either 0 or 1 */
+ is_endpoint = (is_endpoint != 0);
+ BDK_CSR_MODIFY(c, node, BDK_RST_CTLX(pem),
+ c.s.prst_link = 0; /* Link down doesn't automatically assert PERST */
+ c.s.rst_link = is_endpoint; /* Link down automatically assert soft reset for EP */
+ c.s.rst_drv = !is_endpoint; /* PERST is output for RC, input for EP */
+ c.s.rst_rcv = is_endpoint; /* Only read PERST in EP mode */
+ c.s.rst_chip = 0); /* PERST doesn't pull CHIP_RESET */
+
+ if (is_endpoint)
+ {
+ /* If we're configuring an endpoint manually the PEM will not
+ be turned on by default by the hardware. Turn it on now */
+ BDK_CSR_INIT(pemx_on, node, BDK_PEMX_ON(pem));
+ if (!pemx_on.s.pemon)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_PEMX_CLK_EN(pem),
+ c.cn83xx.pceclk_gate = 0;
+ c.cn83xx.csclk_gate = 0);
+ BDK_CSR_MODIFY(c, node, BDK_PEMX_ON(pem),
+ c.s.pemon = 1);
+ }
+ }
+}
+
+/**
+ * Measure the reference clock of a QLM
+ *
+ * @param qlm QLM to measure
+ *
+ * @return Clock rate in Hz
+ */
+int __bdk_qlm_measure_refclock(bdk_node_t node, int qlm)
+{
+ /* Clear the counter */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_EVT_CTRL(qlm),
+ c.s.enb = 0;
+ c.s.clr = 1);
+ bdk_wait_usec(1); /* Give counter a chance to clear */
+ if (BDK_CSR_READ(node, BDK_GSERX_REFCLK_EVT_CNTR(qlm)))
+ bdk_error("GSER%d: Ref clock counter not zero\n", qlm);
+ /* Start counting */
+ uint64_t start = bdk_clock_get_count(BDK_CLOCK_TIME);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_EVT_CTRL(qlm),
+ c.s.enb = 1;
+ c.s.clr = 0);
+ /* Wait for a short time to get a number of counts */
+ bdk_wait_usec(20000); /* 20ms */
+ /* Stop counting */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_EVT_CTRL(qlm),
+ c.s.enb = 0);
+ uint64_t stop = bdk_clock_get_count(BDK_CLOCK_TIME);
+ bdk_wait_usec(1); /* Give counter a chance to stabalize */
+
+ /* Calculate the rate */
+ uint64_t count = BDK_CSR_READ(node, BDK_GSERX_REFCLK_EVT_CNTR(qlm));
+ count *= bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME);
+ count /= stop - start;
+ return count;
+}
+
+/**
+ * Put a QLM into hardware reset
+ *
+ * @param node Node to use in a numa setup
+ * @param qlm QLM to use
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_reset(bdk_node_t node, int qlm)
+{
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_reset = 1);
+ return 0;
+}
+
+/**
+ * Enable PRBS on a QLM
+ *
+ * @param node Node to use in a numa setup
+ * @param qlm QLM to use
+ * @param prbs PRBS mode (31, etc)
+ * @param dir Directions to enable. This is so you can enable TX and later
+ * enable RX after TX has run for a time
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_enable_prbs(bdk_node_t node, int qlm, int prbs, bdk_qlm_direction_t dir)
+{
+ const int NUM_LANES = bdk_qlm_get_lanes(node, qlm);
+ int mode;
+ switch (prbs)
+ {
+ case 31:
+ mode = 1;
+ break;
+ case 23:
+ mode = 2; /* Or 3? */
+ break;
+ case 16:
+ mode = 4;
+ break;
+ case 15:
+ mode = 5;
+ break;
+ case 11:
+ mode = 6;
+ break;
+ case 7:
+ mode = 7;
+ break;
+ default:
+ mode = prbs & 0xff;
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ BDK_CSR_WRITE(node, BDK_GSERX_LANEX_LBERT_PAT_CFG(qlm, lane), prbs >> 8);
+ BDK_TRACE(QLM, "Using mode 0x%x with custom pattern 0x%x\n", mode, prbs >> 8);
+ break;
+ }
+
+ /* For some reason PRBS doesn't work if GSER is configured for PCIe.
+ Disconnect PCIe when we start PRBS */
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+ if (gserx_cfg.s.pcie)
+ {
+ gserx_cfg.s.pcie = 0;
+ BDK_CSR_WRITE(node, BDK_GSERX_CFG(qlm), gserx_cfg.u);
+ bdk_warn("N%d.QLM%d: Disabling PCIe for PRBS/pattern generation\n", node, qlm);
+ }
+ /* For some reason PRBS doesn't work if GSER is configured for SATA.
+ Disconnect SATA when we start PRBS */
+ if (gserx_cfg.s.sata)
+ {
+ gserx_cfg.s.sata = 0;
+ BDK_CSR_WRITE(node, BDK_GSERX_CFG(qlm), gserx_cfg.u);
+ bdk_warn("N%d.QLM%d: Disabling SATA for PRBS/pattern generation\n", node, qlm);
+ bdk_warn("N%d.QLM%d: SATA PRBS/patterns always run at 6G\n", node, qlm);
+ }
+
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_PHY_CTL(qlm),
+ c.s.phy_reset = 0);
+
+ if (dir & BDK_QLM_DIRECTION_TX)
+ {
+ /* Disable first in case already running */
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pg_en = 0);
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pg_en = 1; /* Enable generator */
+ c.s.lbert_pg_width = 3; /* 20 bit */
+ c.s.lbert_pg_mode = mode);
+ }
+
+ if (dir & BDK_QLM_DIRECTION_RX)
+ {
+ /* Clear the error counter and Disable the matcher */
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ {
+ prbs_errors[qlm][lane] = 0;
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pm_en = 0);
+ }
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pm_en = 1; /* Enable matcher */
+ c.s.lbert_pm_width = 3; /* 20 bit */
+ c.s.lbert_pm_mode = mode);
+ }
+ /* Tell the matcher to start sync */
+ for (int retry=0; retry < 4; retry++)
+ {
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pm_sync_start = 1);
+ }
+ /* Wait 10ms */
+ bdk_wait_usec(10000);
+ }
+ }
+ return 0;
+}
+
+/**
+ * Disable PRBS on a QLM
+ *
+ * @param node Node to use in a numa setup
+ * @param qlm QLM to use
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_disable_prbs(bdk_node_t node, int qlm)
+{
+ const int NUM_LANES = bdk_qlm_get_lanes(node, qlm);
+ BDK_CSR_INIT(phy_ctl, node, BDK_GSERX_PHY_CTL(qlm));
+ if (phy_ctl.s.phy_reset)
+ return -1;
+
+ for (int lane = 0; lane < NUM_LANES; lane++)
+ {
+ prbs_errors[qlm][lane] = 0;
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pg_en = 0;
+ c.s.lbert_pm_en = 0);
+ }
+ return 0;
+}
+
+/**
+ * Return the number of PRBS errors since PRBS started running
+ *
+ * @param node Node to use in numa setup
+ * @param qlm QLM to use
+ * @param lane Which lane
+ * @param clear Clear counter after return the current value
+ *
+ * @return Number of errors
+ */
+uint64_t __bdk_qlm_get_prbs_errors(bdk_node_t node, int qlm, int lane, int clear)
+{
+ /* Restart synchronization */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pm_sync_start = 1);
+ /* This CSR is self clearing per the CSR description, but it doesn't
+ seem to do that. Instead it clears when we trigger sync again */
+ BDK_CSR_INIT(rx, node, BDK_GSERX_LANEX_LBERT_ECNT(qlm, lane));
+ uint64_t errors = rx.s.lbert_err_cnt;
+ if (rx.s.lbert_err_ovbit14)
+ errors <<= 7;
+ prbs_errors[qlm][lane] += errors;
+ uint64_t result = prbs_errors[qlm][lane];
+ if (clear)
+ prbs_errors[qlm][lane] = 0;
+ return result;
+}
+
+/**
+ * Inject an error into PRBS
+ *
+ * @param node Node to use in numa setup
+ * @param qlm QLM to use
+ * @param lane Which lane
+ */
+void __bdk_qlm_inject_prbs_error(bdk_node_t node, int qlm, int lane)
+{
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_LBERT_CFG(qlm, lane),
+ c.s.lbert_pg_err_insert = 1);
+}
+
+/**
+ * Enable shallow loopback on a QLM
+ *
+ * @param node Node to use in a numa setup
+ * @param qlm QLM to use
+ * @param loop Type of loopback. Not all QLMs support all modes
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_enable_loop(bdk_node_t node, int qlm, bdk_qlm_loop_t loop)
+{
+ bdk_error("Chip doesn't support shallow QLM loopback\n");
+ return -1;
+}
+
+/**
+ * Initialize the QLM mode table
+ *
+ * @param node Node to initialize
+ * @param qlm Which QLM
+ * @param ref_clk Reference clock of the QLM in Hz
+ */
+void __bdk_qlm_init_mode_table(bdk_node_t node, int qlm, int ref_clk)
+{
+ /* The QLM PLLs are controlled by an array of parameters indexed
+ by the QLM mode for each QLM. We need to fill in these tables.
+ Also each lane has some mode parameters, again in a array index
+ by the lane_mode */
+ for (int lane_mode = 0; lane_mode < 12; lane_mode++)
+ {
+ /* The values used below are all from
+ http://mawiki.caveonetworks.com/wiki/78xx/GSER_WEST */
+ BDK_CSR_INIT(pll_mode_0 , node, BDK_GSERX_PLL_PX_MODE_0(qlm, lane_mode));
+ BDK_CSR_INIT(pll_mode_1 , node, BDK_GSERX_PLL_PX_MODE_1(qlm, lane_mode));
+ BDK_CSR_INIT(lane_mode_0, node, BDK_GSERX_LANE_PX_MODE_0(qlm, lane_mode));
+ BDK_CSR_INIT(lane_mode_1, node, BDK_GSERX_LANE_PX_MODE_1(qlm, lane_mode));
+ switch (lane_mode)
+ {
+ case BDK_GSER_LMODE_E_R_25G_REFCLK100:
+ case BDK_GSER_LMODE_E_R_5G_REFCLK100:
+ case BDK_GSER_LMODE_E_R_8G_REFCLK100:
+ /* These modes are used for PCIe where the defaults are
+ correct. Skip programming these */
+ continue;
+ case BDK_GSER_LMODE_E_R_125G_REFCLK15625_KX:
+ pll_mode_0.s.pll_icp = 0x1;
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0x28;
+
+ pll_mode_1.s.pll_16p5en = 0x1;
+ pll_mode_1.s.pll_cpadj = 0x3;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ pll_mode_1.s.pll_div = 0x10;
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x2;
+ lane_mode_0.s.rx_ldiv = 0x2;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x1;
+ lane_mode_1.s.cdr_fgain = 0xc;
+ lane_mode_1.s.ph_acc_adj = 0x1e;
+ break;
+ case BDK_GSER_LMODE_E_R_3125G_REFCLK15625_XAUI:
+ pll_mode_0.s.pll_icp = 0x1;
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0x14;
+
+ pll_mode_1.s.pll_16p5en = 0x1;
+ pll_mode_1.s.pll_cpadj = 0x2;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ pll_mode_1.s.pll_div = 0x14;
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x1;
+ lane_mode_0.s.rx_ldiv = 0x1;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x1;
+ lane_mode_1.s.cdr_fgain = 0xc;
+ lane_mode_1.s.ph_acc_adj = 0x1e;
+ break;
+ case BDK_GSER_LMODE_E_R_103125G_REFCLK15625_KR:
+ pll_mode_0.s.pll_icp = 0x1;
+ pll_mode_0.s.pll_rloop = 0x5;
+ pll_mode_0.s.pll_pcs_div = 0xa;
+
+ pll_mode_1.s.pll_16p5en = 0x1;
+ pll_mode_1.s.pll_cpadj = 0x2;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x1;
+ pll_mode_1.s.pll_div = 0x21;
+
+ lane_mode_0.s.ctle = 0x3;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x0;
+ lane_mode_0.s.rx_ldiv = 0x0;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x1;
+ lane_mode_1.s.vma_mm = 0x0;
+ lane_mode_1.s.cdr_fgain = 0xa;
+ lane_mode_1.s.ph_acc_adj = 0xf;
+ break;
+ case BDK_GSER_LMODE_E_R_125G_REFCLK15625_SGMII:
+ pll_mode_0.s.pll_icp = 0x1;
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0x28;
+
+ pll_mode_1.s.pll_16p5en = 0x1;
+ pll_mode_1.s.pll_cpadj = 0x3;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ pll_mode_1.s.pll_div = 0x10;
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x2;
+ lane_mode_0.s.rx_ldiv = 0x2;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x1;
+ lane_mode_1.s.cdr_fgain = 0xc;
+ lane_mode_1.s.ph_acc_adj = 0x1e;
+ if(ref_clk == REF_100MHZ)
+ {
+ pll_mode_0.s.pll_pcs_div = 0x28;
+ pll_mode_1.s.pll_div = 0x19;
+ pll_mode_1.s.pll_cpadj = 0x2;
+ }
+ break;
+ case BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII:
+ pll_mode_0.s.pll_icp = 0x1; /* Per Scott McIlhenny 5/17/2016 (t81) */
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0xa;
+
+ pll_mode_1.s.pll_16p5en = 0x0;
+ pll_mode_1.s.pll_cpadj = 0x2;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ /* QSGMII is a special case. We use the same table entry for
+ 100Mhz and 125Mhz clocks as the normal 156Mhz */
+ switch (ref_clk)
+ {
+ case REF_100MHZ:
+ pll_mode_1.s.pll_div = 0x19;
+ break;
+ case REF_125MHZ:
+ pll_mode_1.s.pll_div = 0x14;
+ break;
+ default: /* REF_156MHZ */
+ pll_mode_1.s.pll_div = 0x10;
+ break;
+ }
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x0;
+ lane_mode_0.s.rx_ldiv = 0x0;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x1; /* Per Scott McIlhenny 5/17/2016 (t81) */
+ lane_mode_1.s.cdr_fgain = 0xc;
+ lane_mode_1.s.ph_acc_adj = 0x1e;
+ break;
+ case BDK_GSER_LMODE_E_R_625G_REFCLK15625_RXAUI:
+ pll_mode_0.s.pll_icp = 0x1;
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0xa;
+
+ pll_mode_1.s.pll_16p5en = 0x0;
+ pll_mode_1.s.pll_cpadj = 0x2;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ pll_mode_1.s.pll_div = 0x14;
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x0;
+ lane_mode_0.s.rx_ldiv = 0x0;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x0;
+ lane_mode_1.s.cdr_fgain = 0xa;
+ lane_mode_1.s.ph_acc_adj = 0x14;
+ break;
+ case BDK_GSER_LMODE_E_R_25G_REFCLK125:
+ pll_mode_0.s.pll_icp = 0x3;
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0x5;
+
+ pll_mode_1.s.pll_16p5en = 0x0;
+ pll_mode_1.s.pll_cpadj = 0x1;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ pll_mode_1.s.pll_div = 0x14;
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x1;
+ lane_mode_0.s.tx_ldiv = 0x1;
+ lane_mode_0.s.rx_ldiv = 0x1;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x1;
+ lane_mode_1.s.cdr_fgain = 0xa;
+ lane_mode_1.s.ph_acc_adj = 0x14;
+ break;
+ case BDK_GSER_LMODE_E_R_5G_REFCLK125:
+ pll_mode_0.s.pll_icp = 0x3;
+ pll_mode_0.s.pll_rloop = 0x3;
+ pll_mode_0.s.pll_pcs_div = 0xa;
+
+ pll_mode_1.s.pll_16p5en = 0x0;
+ pll_mode_1.s.pll_cpadj = 0x1;
+ pll_mode_1.s.pll_pcie3en = 0x0;
+ pll_mode_1.s.pll_opr = 0x0;
+ pll_mode_1.s.pll_div = 0x14;
+
+ lane_mode_0.s.ctle = 0x0;
+ lane_mode_0.s.pcie = 0x1;
+ lane_mode_0.s.tx_ldiv = 0x0;
+ lane_mode_0.s.rx_ldiv = 0x0;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x3;
+ lane_mode_0.s.rx_mode = 0x3;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x0;
+ lane_mode_1.s.cdr_fgain = 0xa;
+ lane_mode_1.s.ph_acc_adj = 0x14;
+ break;
+ case BDK_GSER_LMODE_E_R_8G_REFCLK125:
+ pll_mode_0.s.pll_icp = 0x2;
+ pll_mode_0.s.pll_rloop = 0x5;
+ pll_mode_0.s.pll_pcs_div = 0xa;
+
+ pll_mode_1.s.pll_16p5en = 0x0;
+ pll_mode_1.s.pll_cpadj = 0x1;
+ pll_mode_1.s.pll_pcie3en = 0x1;
+ pll_mode_1.s.pll_opr = 0x1;
+ pll_mode_1.s.pll_div = 0x20;
+
+ lane_mode_0.s.ctle = 0x3;
+ lane_mode_0.s.pcie = 0x0;
+ lane_mode_0.s.tx_ldiv = 0x0;
+ lane_mode_0.s.rx_ldiv = 0x0;
+ lane_mode_0.s.srate = 0x0;
+ lane_mode_0.s.tx_mode = 0x2;
+ lane_mode_0.s.rx_mode = 0x2;
+
+ lane_mode_1.s.vma_fine_cfg_sel = 0x0;
+ lane_mode_1.s.vma_mm = 0x0;
+ lane_mode_1.s.cdr_fgain = 0xb;
+ lane_mode_1.s.ph_acc_adj = 0x23;
+ break;
+ }
+ BDK_CSR_WRITE(node, BDK_GSERX_PLL_PX_MODE_0(qlm, lane_mode), pll_mode_0.u);
+ BDK_CSR_WRITE(node, BDK_GSERX_PLL_PX_MODE_1(qlm, lane_mode), pll_mode_1.u);
+ BDK_CSR_WRITE(node, BDK_GSERX_LANE_PX_MODE_0(qlm, lane_mode), lane_mode_0.u);
+ BDK_CSR_WRITE(node, BDK_GSERX_LANE_PX_MODE_1(qlm, lane_mode), lane_mode_1.u);
+ }
+}
+
+/**
+ * Given a valid PEM number, return its speed in Gbaud
+ *
+ * @param node Node to use in numa setup
+ * @param pem PEM to get speed of
+ *
+ * @return Speed in Gbaud. Zero if disabled
+ */
+int __bdk_qlm_get_gbaud_mhz_pem(bdk_node_t node, int pem)
+{
+ BDK_CSR_INIT(pem_cfg, node, BDK_PEMX_CFG(pem));
+ switch (pem_cfg.cn83xx.md)
+ {
+ case 0: /* Gen 1 */
+ return 2500;
+ case 1: /* Gen 2 */
+ return 5000;
+ case 2: /* Gen 3 */
+ return 8000;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Get the speed of a QLM using its LMODE. This can't be used on PCIe QLMs.
+ *
+ * @param node Node to use in numa setup
+ * @param qlm Which QLM
+ *
+ * @return QLM speed on Gbaud
+ */
+int __bdk_qlm_get_gbaud_mhz_lmode(bdk_node_t node, int qlm)
+{
+ /* QLM is not in PCIe, assume LMODE is good enough for determining
+ the speed */
+ BDK_CSR_INIT(lane_mode, node, BDK_GSERX_LANE_MODE(qlm));
+ switch (lane_mode.s.lmode)
+ {
+ case BDK_GSER_LMODE_E_R_25G_REFCLK100:
+ return 2500;
+ case BDK_GSER_LMODE_E_R_5G_REFCLK100:
+ return 5000;
+ case BDK_GSER_LMODE_E_R_8G_REFCLK100:
+ return 8000;
+ case BDK_GSER_LMODE_E_R_125G_REFCLK15625_KX:
+ return 1250;
+ case BDK_GSER_LMODE_E_R_3125G_REFCLK15625_XAUI:
+ return 3125;
+ case BDK_GSER_LMODE_E_R_103125G_REFCLK15625_KR:
+ return 10312;
+ case BDK_GSER_LMODE_E_R_125G_REFCLK15625_SGMII:
+ return 1250;
+ case BDK_GSER_LMODE_E_R_5G_REFCLK15625_QSGMII:
+ return 5000;
+ case BDK_GSER_LMODE_E_R_625G_REFCLK15625_RXAUI:
+ return 6250;
+ case BDK_GSER_LMODE_E_R_25G_REFCLK125:
+ return 2500;
+ case BDK_GSER_LMODE_E_R_5G_REFCLK125:
+ return 5000;
+ case BDK_GSER_LMODE_E_R_8G_REFCLK125:
+ return 8000;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Converts a measured reference clock to a likely ideal value. Rounds
+ * clock speed to the nearest REF_*Mhz define.
+ *
+ * @param node Node to use in numa setup
+ * @param qlm Which QLM
+ * @param measured_hz
+ * Measured value
+ *
+ * @return Value exactly matching a define
+ */
+int __bdk_qlm_round_refclock(bdk_node_t node, int qlm, int measured_hz)
+{
+ int ref_clk;
+ if ((measured_hz > REF_100MHZ - REF_100MHZ / 10) && (measured_hz < REF_100MHZ + REF_100MHZ / 10))
+ {
+ ref_clk = REF_100MHZ;
+ }
+ else if ((measured_hz > REF_125MHZ - REF_125MHZ / 10) && (measured_hz < REF_125MHZ + REF_125MHZ / 10))
+ {
+ ref_clk = REF_125MHZ;
+ }
+ else if ((measured_hz > REF_156MHZ - REF_156MHZ / 10) && (measured_hz < REF_156MHZ + REF_156MHZ / 10))
+ {
+ ref_clk = REF_156MHZ;
+ }
+ else if (measured_hz < 1000000)
+ {
+ ref_clk = 0; /* Used for disabled QLMs */
+ }
+ else
+ {
+ ref_clk = measured_hz;
+ bdk_error("N%d.QLM%d: Unexpected reference clock speed of %d Mhz\n", node, qlm, measured_hz / 1000000);
+ }
+ return ref_clk;
+}
+
+/**
+ * TWSI reads from the MCU randomly timeout. Retry a few times on
+ * failure to try and recover
+ *
+ * @param node Node to use in a Numa setup. Can be an exact ID or a special
+ * value.
+ * @param twsi_id which TWSI bus to use
+ * @param dev_addr Device address (7 bit)
+ * @param internal_addr
+ * Internal address. Can be 0, 1 or 2 bytes in width
+ * @param num_bytes Number of data bytes to read (1-4)
+ * @param ia_width_bytes
+ * Internal address size in bytes (0, 1, or 2)
+ *
+ * @return Read data, or -1 on failure
+ */
+static int64_t mcu_read(bdk_node_t node, int twsi_id, uint8_t dev_addr, uint16_t internal_addr, int num_bytes, int ia_width_bytes)
+{
+ int read_tries = 0;
+ int64_t result;
+ do
+ {
+ result = bdk_twsix_read_ia(node, twsi_id, dev_addr, internal_addr, num_bytes, ia_width_bytes);
+ read_tries++;
+ if (result < 0)
+ {
+ BDK_TRACE(QLM, "Timeout %d reading from MCU\n", read_tries);
+ bdk_wait_usec(100000);
+ }
+ } while ((result < 0) && (read_tries < 3));
+ return result;
+}
+
+static void __bdk_qlm_set_reference(bdk_node_t node, int qlm, int ref_clk)
+{
+ int use_clock;
+ if (CAVIUM_IS_MODEL(CAVIUM_CN88XX) || CAVIUM_IS_MODEL(CAVIUM_CN83XX) || CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+ {
+ switch (ref_clk)
+ {
+ case REF_100MHZ:
+ use_clock = 0; /* Common clock 0 */
+ BDK_TRACE(QLM, "Setting N%d.QLM%d to use common clock 0\n", node, qlm);
+ break;
+ case REF_156MHZ:
+ use_clock = 1; /* Common clock 1 */
+ BDK_TRACE(QLM, "Setting N%d.QLM%d to use common clock 1\n", node, qlm);
+ break;
+ default:
+ use_clock = 2; /* External clock */
+ BDK_TRACE(QLM, "Setting N%d.QLM%d to use external clock\n", node, qlm);
+ break;
+ }
+ }
+ else
+ {
+ bdk_error("Update __bdk_qlm_set_reference() for qlm auto config of this chip\n");
+ return;
+ }
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_REFCLK_SEL(qlm),
+ c.s.com_clk_sel = (use_clock != 2);
+ c.s.use_com1 = (use_clock == 1));
+}
+
+/**
+ * For Cavium EVB and EBB board, query the MCU to determine the QLM setup. Applying
+ * any configuration found.
+ *
+ * @param node Node to configure
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_mcu_auto_config(bdk_node_t node)
+{
+ const int MCU_TWSI_BUS = 0;
+ const int MCU_TWSI_ADDRESS = 0x60;
+ int64_t data;
+
+ /* Check the two magic number bytes the MCU should return */
+ data = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x00, 1, 1);
+ if (data != 0xa5)
+ {
+ printf("QLM Config: MCU not found, skipping auto configuration\n");
+ return -1;
+ }
+ data = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x01, 1, 1);
+ if (data != 0x5a)
+ {
+ bdk_error("QLM Config: MCU magic number incorrect\n");
+ return -1;
+ }
+
+ /* Read the MCU version */
+ int mcu_major = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x02, 1, 1);
+ int mcu_minor = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x03, 1, 1);
+ BDK_TRACE(QLM, "MCU version %d.%d\n", mcu_major, mcu_minor);
+ if ((mcu_major < 2) || ((mcu_major == 2) && (mcu_minor < 30)))
+ {
+ bdk_error("QLM Config: Unexpected MCU version %d.%d\n", mcu_major, mcu_minor);
+ return -1;
+ }
+
+ /* Find out how many lanes the MCU thinks are available */
+ int lanes = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x16, 1, 1);
+ BDK_TRACE(QLM, "MCU says board has %d lanes\n", lanes);
+ int correct_lanes = 0;
+ if (cavium_is_altpkg(CAVIUM_CN88XX))
+ correct_lanes = 22;
+ else if (CAVIUM_IS_MODEL(CAVIUM_CN88XX))
+ correct_lanes = 32;
+ else if (CAVIUM_IS_MODEL(CAVIUM_CN83XX))
+ correct_lanes = 22;
+ else if (CAVIUM_IS_MODEL(CAVIUM_CN81XX))
+ correct_lanes = 8;
+ if (lanes != correct_lanes)
+ {
+ bdk_error("QLM Config: Unexpected number of lanes (%d) from MCU\n", lanes);
+ return -1;
+ }
+
+ int lane = 0;
+ int qlm = 0;
+ while (lane < lanes)
+ {
+ int write_status;
+ int width;
+ int mode;
+ int speed;
+ int refclk;
+ /* TWSI reads from the MCU randomly timeout. Retry a few times on
+ failure to try and recover */
+ int read_tries = 0;
+ do
+ {
+ read_tries++;
+ if (read_tries > 3)
+ {
+ bdk_error("QLM Config: Timeouts reading from MCU\n");
+ return -1;
+ }
+ /* Space request out 20ms */
+ bdk_wait_usec(20000);
+ /* Select the lane we are interested in */
+ write_status = bdk_twsix_write_ia(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x16, 1, 1, lane);
+ /* Space request out 20ms */
+ bdk_wait_usec(20000);
+ /* Get the mode */
+ width = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x17, 1, 1);
+ mode = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x18, 2, 1);
+ speed = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x19, 2, 1);
+ refclk = mcu_read(node, MCU_TWSI_BUS, MCU_TWSI_ADDRESS, 0x1a, 1, 1);
+ } while ((write_status < 0) || (width < 0) || (mode < 0) || (speed < 0) || (refclk < 0));
+
+ BDK_TRACE(QLM, "MCU lane %d, width %d, mode 0x%x, speed 0x%x, ref 0x%x\n",
+ lane, width, mode, speed, refclk);
+ if ((width != 0) && (width != 1) && (width != 2) && (width != 4) && (width != 8))
+ {
+ bdk_error("QLM Config: Unexpected interface width (%d) from MCU\n", width);
+ return -1;
+ }
+ /* MCU reports a width of 0 for unconfigured QLMs. It reports a width
+ of 1 for some combinations on CN80XX, and two on others. Convert
+ either 0 or 1 to the actual width, or 2 for CN80XX. Yuck */
+ if ((width == 0) || (width == 1))
+ {
+ if (cavium_is_altpkg(CAVIUM_CN81XX) && (qlm < 2))
+ width = 2;
+ else
+ width = bdk_qlm_get_lanes(node, qlm);
+ }
+ bdk_qlm_modes_t qlm_mode;
+ int qlm_speed = (speed >> 8) * 1000 + (speed & 0xff) * 1000 / 256;
+ int use_ref = 0;
+ bdk_qlm_mode_flags_t qlm_flags = 0;
+ if (mode < 0x4000)
+ {
+ switch (mode)
+ {
+ case 0x0000: /* No Configuration */
+ qlm_mode = BDK_QLM_MODE_DISABLED;
+ break;
+ case 0x0101: /* PCIe Host */
+ qlm_mode = (width == 8) ? BDK_QLM_MODE_PCIE_1X8 :
+ (width == 4) ? BDK_QLM_MODE_PCIE_1X4 :
+ BDK_QLM_MODE_PCIE_1X2;
+ use_ref = REF_100MHZ;
+ break;
+ case 0x0102: /* PCIe Endpoint */
+ qlm_mode = (width == 8) ? BDK_QLM_MODE_PCIE_1X8 :
+ (width == 4) ? BDK_QLM_MODE_PCIE_1X4 :
+ BDK_QLM_MODE_PCIE_1X2;
+ qlm_flags = BDK_QLM_MODE_FLAG_ENDPOINT;
+ use_ref = 0; /* Use the external reference for EP mode */
+ break;
+ case 0x1000: /* SGMII */
+ qlm_mode = (width == 4) ? BDK_QLM_MODE_SGMII_4X1 :
+ (width == 2) ? BDK_QLM_MODE_SGMII_2X1 :
+ BDK_QLM_MODE_SGMII_1X1;
+ use_ref = REF_156MHZ;
+ /* CN80XX parts on EBBs use phy port 2 for SGMII, while QSGMII
+ uses the correct port. Fix this for DLM1 and DLM3 */
+ if (cavium_is_altpkg(CAVIUM_CN81XX))
+ {
+ int bgx = (qlm == 3) ? 1 : 0;
+ uint64_t phy = bdk_config_get_int(BDK_CONFIG_PHY_ADDRESS, 0, bgx, 2);
+ bdk_config_set_int(phy, BDK_CONFIG_PHY_ADDRESS, 0, bgx, 1);
+ }
+ break;
+ case 0x1100: /* QSGMII */
+ qlm_mode = BDK_QLM_MODE_QSGMII_4X1;
+ use_ref = REF_100MHZ;
+ break;
+ case 0x2000: /* XAUI */
+ qlm_mode = BDK_QLM_MODE_XAUI_1X4;
+ use_ref = REF_156MHZ;
+ break;
+ case 0x2100: /* RXAUI */
+ qlm_mode = (width == 2) ? BDK_QLM_MODE_RXAUI_1X2 : BDK_QLM_MODE_RXAUI_2X2;
+ use_ref = REF_156MHZ;
+ break;
+ case 0x2200: /* DXAUI */
+ qlm_mode = BDK_QLM_MODE_XAUI_1X4;
+ use_ref = REF_156MHZ;
+ break;
+ case 0x3001: /* Interlaken */
+ qlm_mode = BDK_QLM_MODE_ILK;
+ use_ref = REF_156MHZ;
+ break;
+ default:
+ bdk_error("QLM Config: Unexpected interface mode (0x%x) from MCU\n", mode);
+ qlm_mode = BDK_QLM_MODE_DISABLED;
+ break;
+ }
+ }
+ else
+ {
+ switch (mode)
+ {
+ case 0x4000: /* SATA */
+ qlm_mode = (width == 2) ? BDK_QLM_MODE_SATA_2X1 : BDK_QLM_MODE_SATA_4X1;
+ use_ref = REF_100MHZ;
+ break;
+ case 0x5001: /* XFI */
+ qlm_mode = (width == 4) ? BDK_QLM_MODE_XFI_4X1 :
+ (width == 2) ? BDK_QLM_MODE_XFI_2X1 :
+ BDK_QLM_MODE_XFI_1X1;
+ use_ref = REF_156MHZ;
+ break;
+ case 0x5002: /* 10G-KR */
+ qlm_mode = (width == 4) ? BDK_QLM_MODE_10G_KR_4X1 :
+ (width == 2) ? BDK_QLM_MODE_10G_KR_2X1 :
+ BDK_QLM_MODE_10G_KR_1X1;
+ use_ref = REF_156MHZ;
+ break;
+ case 0x6001: /* XLAUI */
+ qlm_mode = BDK_QLM_MODE_XLAUI_1X4;
+ use_ref = REF_156MHZ;
+ break;
+ case 0x6002: /* 40G-KR4 */
+ qlm_mode = BDK_QLM_MODE_40G_KR4_1X4;
+ use_ref = REF_156MHZ;
+ break;
+ default:
+ bdk_error("QLM Config: Unexpected interface mode (0x%x) from MCU\n", mode);
+ qlm_mode = BDK_QLM_MODE_DISABLED;
+ break;
+ }
+ }
+ lane += width;
+ do
+ {
+ int internal_qlm = qlm;
+ /* Alternate package parts have different QLM numbers for internal
+ versus external. The MCU uses the external numbers */
+ if (cavium_is_altpkg(CAVIUM_CN88XX))
+ {
+ switch (qlm)
+ {
+ case 0: /* QLM0 -> QLM4 */
+ internal_qlm = 4;
+ break;
+ case 1: /* QLM1 -> QLM5 */
+ internal_qlm = 5;
+ break;
+ case 2: /* QLM2 -> QLM0 */
+ internal_qlm = 0;
+ break;
+ case 3: /* QLM3 -> QLM1 */
+ internal_qlm = 1;
+ break;
+ case 4: /* DLM4 -> QLM2 */
+ internal_qlm = 2;
+ break;
+ case 5: /* DLM5 -> QLM6 */
+ internal_qlm = 6;
+ break;
+ case 6: /* DLM6 -> QLM7 */
+ internal_qlm = 7;
+ break;
+ default:
+ bdk_error("Invalid external QLM%d from MCU\n", qlm);
+ return -1;
+ }
+ }
+ if (qlm_flags & BDK_QLM_MODE_FLAG_ENDPOINT)
+ {
+ BDK_TRACE(QLM, "Skipping N%d.QLM%d mode %s(%d), speed %d, flags 0x%x (EP should already be setup)\n",
+ node, internal_qlm, bdk_qlm_mode_tostring(qlm_mode), qlm_mode, qlm_speed, qlm_flags);
+ }
+ else
+ {
+ BDK_TRACE(QLM, "Setting N%d.QLM%d mode %s(%d), speed %d, flags 0x%x\n",
+ node, internal_qlm, bdk_qlm_mode_tostring(qlm_mode), qlm_mode, qlm_speed, qlm_flags);
+ /* Set the reference clock for this QLM */
+ __bdk_qlm_set_reference(node, internal_qlm, use_ref);
+ if (bdk_qlm_set_mode(node, internal_qlm, qlm_mode, qlm_speed, qlm_flags))
+ return -1;
+ }
+ int num_lanes = bdk_qlm_get_lanes(node, internal_qlm);
+ /* CN86XX looks like two lanes each for DLM4-7 */
+ if (cavium_is_altpkg(CAVIUM_CN88XX) && (qlm >= 4))
+ num_lanes = 2;
+ if (qlm_mode == BDK_QLM_MODE_PCIE_1X8)
+ {
+ /* PCIe x8 is a special case as the QLM config function
+ actually configures both QLMs in one go */
+ qlm++;
+ width -= 8;
+ }
+ else if ((qlm_mode == BDK_QLM_MODE_PCIE_1X4) && (width > num_lanes))
+ {
+ /* PCIe x4 is a special case as the QLM config function
+ actually configures both QLMs in one go */
+ qlm++;
+ width -= 4;
+ }
+ else if (width >= num_lanes)
+ {
+ if (num_lanes == 1)
+ width -= 2; /* Special case for CN80XX */
+ else
+ width -= num_lanes;
+ }
+ else
+ width = 0;
+ qlm++;
+ } while (width > 0);
+ }
+ return 0;
+}
+
+/**
+ * Display the current settings of a QLM lane
+ *
+ * @param node Node the QLM is on
+ * @param qlm QLM to display
+ * @param qlm_lane Lane to use
+ * @param show_tx Display TX parameters
+ * @param show_rx Display RX parameters
+ */
+void bdk_qlm_display_settings(bdk_node_t node, int qlm, int qlm_lane, bool show_tx, bool show_rx)
+{
+ const char *dir_label[] = {"Hold", "Inc", "Dec", "Hold"};
+
+ uint64_t rx_aeq_out_0 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_AEQ_OUT_0(qlm, qlm_lane));
+ uint64_t rx_aeq_out_1 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_AEQ_OUT_1(qlm, qlm_lane));
+ uint64_t rx_aeq_out_2 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_AEQ_OUT_2(qlm, qlm_lane));
+ uint64_t rx_vma_status_0 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_VMA_STATUS_0(qlm, qlm_lane));
+ uint64_t rx_vma_status_1 = BDK_CSR_READ(node, BDK_GSERX_LANEX_RX_VMA_STATUS_1(qlm, qlm_lane));
+ uint64_t sds_pin_mon_1 = BDK_CSR_READ(node, BDK_GSERX_LANEX_SDS_PIN_MON_1(qlm, qlm_lane));
+ uint64_t sds_pin_mon_2 = BDK_CSR_READ(node, BDK_GSERX_LANEX_SDS_PIN_MON_2(qlm, qlm_lane));
+ uint64_t br_rxx_eer = BDK_CSR_READ(node, BDK_GSERX_BR_RXX_EER(qlm, qlm_lane));
+
+ printf("N%d.QLM%d Lane %d:\n", node, qlm, qlm_lane);
+ if (show_rx)
+ {
+ printf(" DFE Tap 1: %llu, Tap 2: %lld, Tap 3: %lld, Tap 4: %lld, Tap 5: %lld\n",
+ bdk_extract(rx_aeq_out_1, 0, 5),
+ bdk_extract_smag(rx_aeq_out_1, 5, 9),
+ bdk_extract_smag(rx_aeq_out_1, 10, 14),
+ bdk_extract_smag(rx_aeq_out_0, 0, 4),
+ bdk_extract_smag(rx_aeq_out_0, 5, 9));
+ printf(" Pre-CTLE Gain: %llu, Post-CTLE Gain: %llu, CTLE Peak: %llu, CTLE Pole: %llu\n",
+ bdk_extract(rx_aeq_out_2, 4, 4),
+ bdk_extract(rx_aeq_out_2, 0, 4),
+ bdk_extract(rx_vma_status_0, 2, 4),
+ bdk_extract(rx_vma_status_0, 0, 2));
+ printf(" RX Equalization Tx Directions Hints TXPRE: %s, TXMAIN: %s, TXPOST: %s, Figure of Merit: %llu\n",
+ dir_label[bdk_extract(br_rxx_eer, 0, 2)],
+ dir_label[bdk_extract(br_rxx_eer, 2, 2)],
+ dir_label[bdk_extract(br_rxx_eer, 4, 2)],
+ bdk_extract(br_rxx_eer, 6, 8));
+ }
+ if (show_tx)
+ {
+ printf(" TX Swing: %llu, Pre-emphasis Pre-cursor: %llu, Post-cursor: %llu\n",
+ bdk_extract(sds_pin_mon_1, 1, 5),
+ bdk_extract(sds_pin_mon_2, 0, 4),
+ bdk_extract(sds_pin_mon_2, 4, 5));
+ printf(" TX Boost Enable: %llu, TX Turbo Mode: %llu\n",
+ bdk_extract(sds_pin_mon_2, 10, 1),
+ bdk_extract(sds_pin_mon_2, 9, 1));
+ }
+ printf(" Training-done: %llu\n",
+ bdk_extract(rx_vma_status_1, 7, 1));
+}
+
+/**
+ * Perform RX equalization on a QLM
+ *
+ * @param node Node the QLM is on
+ * @param qlm QLM to perform RX equalization on
+ * @param qlm_lane Lane to use, or -1 for all lanes
+ *
+ * @return Zero on success, negative if any lane failed RX equalization
+ */
+int __bdk_qlm_rx_equalization(bdk_node_t node, int qlm, int qlm_lane)
+{
+ /* Don't touch QLMs is reset or powered down */
+ BDK_CSR_INIT(phy_ctl, node, BDK_GSERX_PHY_CTL(qlm));
+ if (phy_ctl.s.phy_pd || phy_ctl.s.phy_reset)
+ return -1;
+ /* Don't run on PCIe links */
+ if (bdk_qlm_get_mode(node, qlm) <= BDK_QLM_MODE_PCIE_1X8)
+ return -1;
+
+ int fail = 0; /* Bitmask of lanes that failed CDR Lock or Eltrical Idle check */
+ int pending = 0; /* Bitmask of lanes that we're waiting for */
+ int MAX_LANES = bdk_qlm_get_lanes(node, qlm);
+
+ BDK_TRACE(QLM, "N%d.QLM%d: Starting RX equalization on lane %d\n", node, qlm, qlm_lane);
+ for (int lane = 0; lane < MAX_LANES; lane++)
+ {
+ /* Skip lanes we don't care about */
+ if ((qlm_lane != -1) && (qlm_lane != lane))
+ continue;
+ /* Check that the lane has completed CDR lock */
+ BDK_CSR_INIT(eie_detsts, node, BDK_GSERX_RX_EIE_DETSTS(qlm));
+ if (((1 << lane) & eie_detsts.s.cdrlock) == 0)
+ {
+ /* Mark bad so we skip this lane below */
+ fail |= 1 << lane;
+ continue;
+ }
+ /* Enable software control */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_BR_RXX_CTL(qlm, lane),
+ c.s.rxt_swm = 1);
+ /* Clear the completion flag and initiate a new request */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_BR_RXX_EER(qlm, lane),
+ c.s.rxt_esv = 0;
+ c.s.rxt_eer = 1);
+ /* Remember that we have to wait for this lane */
+ pending |= 1 << lane;
+ }
+
+ /* Timing a few of these over XFI on CN73XX, each takes 21-23ms. XLAUI
+ was about the same time. DXAUI and RXAUI both took 2-3ms. Put the
+ timeout at 250ms, which is roughly 10x my measurements. */
+ uint64_t timeout = bdk_clock_get_count(BDK_CLOCK_TIME) + bdk_clock_get_rate(node, BDK_CLOCK_TIME) / 4;
+ while (pending)
+ {
+ for (int lane = 0; lane < MAX_LANES; lane++)
+ {
+ int lane_mask = 1 << lane;
+ /* 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 */
+ BDK_CSR_INIT(eie_detsts, node, BDK_GSERX_RX_EIE_DETSTS(qlm));
+ BDK_CSR_INIT(gserx_br_rxx_eer, node, BDK_GSERX_BR_RXX_EER(qlm, lane));
+ /* 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 (gserx_br_rxx_eer.s.rxt_esv)
+ {
+ /* Clear pending if RX equalization finished */
+ pending &= ~lane_mask;
+ }
+ }
+ /* Break out of the loop on timeout */
+ if (bdk_clock_get_count(BDK_CLOCK_TIME) > timeout)
+ break;
+ }
+
+ /* Cleanup and report status */
+ for (int lane = 0; lane < MAX_LANES; lane++)
+ {
+ /* Skip lanes we don't care about */
+ if ((qlm_lane != -1) && (qlm_lane != lane))
+ continue;
+ int lane_mask = 1 << lane;
+ /* Get the final RX equalization status */
+ BDK_CSR_INIT(gserx_br_rxx_eer, node, BDK_GSERX_BR_RXX_EER(qlm, lane));
+ /* Disable software control */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_BR_RXX_CTL(qlm, lane),
+ c.s.rxt_swm = 0);
+ /* Report status */
+ if (fail & lane_mask)
+ {
+ BDK_TRACE(QLM, "N%d.QLM%d: Lane %d RX equalization lost CDR Lock or entered Electrical Idle\n", node, qlm, lane);
+ }
+ else if ((pending & lane_mask) || !gserx_br_rxx_eer.s.rxt_esv)
+ {
+ BDK_TRACE(QLM, "N%d.QLM%d: Lane %d RX equalization timeout\n", node, qlm, lane);
+ fail |= 1 << lane;
+ }
+ else
+ {
+ bdk_qlm_display_settings(node, qlm, lane, false, true);
+ }
+ }
+
+ return (fail) ? -1 : 0;
+}
+
+/**
+ * Configure the TX tuning parameters for a QLM lane. The tuning parameters can
+ * be specified as -1 to maintain their current value
+ *
+ * @param node Node to configure
+ * @param qlm QLM to configure
+ * @param lane Lane to configure
+ * @param tx_swing Transmit swing (coef 0) Range 0-31
+ * @param tx_pre Pre cursor emphasis (Coef -1). Range 0-15
+ * @param tx_post Post cursor emphasis (Coef +1). Range 0-31
+ * @param tx_gain Transmit gain. Range 0-7
+ * @param tx_vboost Transmit voltage boost. Range 0-1
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_tune_lane_tx(bdk_node_t node, int qlm, int lane, int tx_swing, int tx_pre, int tx_post, int tx_gain, int tx_vboost)
+{
+ /* Check tuning constraints */
+ if ((tx_swing < -1) || (tx_swing > 25))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: Invalid TX_SWING(%d)\n", node, qlm, lane, tx_swing);
+ return -1;
+ }
+ if ((tx_pre < -1) || (tx_pre > 10))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: Invalid TX_PRE(%d)\n", node, qlm, lane, tx_pre);
+ return -1;
+ }
+ if ((tx_post < -1) || (tx_post > 15))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: Invalid TX_POST(%d)\n", node, qlm, lane, tx_post);
+ return -1;
+ }
+ if ((tx_pre >= 0) && (tx_post >= 0) && (tx_swing >= 0) && (tx_pre + tx_post - tx_swing > 2))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: TX_PRE(%d) + TX_POST(%d) - TX_SWING(%d) must be less than or equal to 2\n", node, qlm, lane, tx_pre, tx_post, tx_swing);
+ return -1;
+ }
+ if ((tx_pre >= 0) && (tx_post >= 0) && (tx_swing >= 0) && (tx_pre + tx_post + tx_swing > 35))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: TX_PRE(%d) + TX_POST(%d) + TX_SWING(%d) must be less than or equal to 35\n", node, qlm, lane, tx_pre, tx_post, tx_swing);
+ return -1;
+ }
+
+ if ((tx_gain < -1) || (tx_gain > 7))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: Invalid TX_GAIN(%d). TX_GAIN must be between 0 and 7\n", node, qlm, lane, tx_gain);
+ return -1;
+ }
+
+ if ((tx_vboost < -1) || (tx_vboost > 1))
+ {
+ bdk_error("N%d.QLM%d: Lane %d: Invalid TX_VBOOST(%d). TX_VBOOST must be 0 or 1.\n", node, qlm, lane, tx_vboost);
+ return -1;
+ }
+
+ if ((tx_pre != -1) && (tx_post == -1))
+ {
+ BDK_CSR_INIT(emphasis, node, BDK_GSERX_LANEX_TX_PRE_EMPHASIS(qlm, lane));
+ tx_post = emphasis.s.cfg_tx_premptap >> 4;
+ }
+
+ if ((tx_post != -1) && (tx_pre == -1))
+ {
+ BDK_CSR_INIT(emphasis, node, BDK_GSERX_LANEX_TX_PRE_EMPHASIS(qlm, lane));
+ tx_pre = emphasis.s.cfg_tx_premptap & 0xf;
+ }
+
+ BDK_TRACE(QLM, "N%d.QLM%d: Lane %d: TX_SWING=%d, TX_PRE=%d, TX_POST=%d, TX_GAIN=%d, TX_VBOOST=%d\n",
+ node, qlm, lane, tx_swing, tx_pre, tx_post, tx_gain, tx_vboost);
+
+ /* Manual Tx Swing and Tx Equalization Programming Steps */
+
+ /* 1) Enable Tx swing and Tx emphasis overrides */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_1(qlm, lane),
+ c.s.tx_swing_ovrrd_en = (tx_swing != -1);
+ c.s.tx_premptap_ovrrd_val = (tx_pre != -1) && (tx_post != -1);
+ c.s.tx_vboost_en_ovrrd_en = (tx_vboost != -1)); /* Vboost override */
+ /* 2) Program the Tx swing and Tx emphasis Pre-cursor and Post-cursor values */
+ if (tx_swing != -1)
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_0(qlm, lane),
+ c.s.cfg_tx_swing = tx_swing);
+ if ((tx_pre != -1) && (tx_post != -1))
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_PRE_EMPHASIS(qlm, lane),
+ c.s.cfg_tx_premptap = (tx_post << 4) | tx_pre);
+ /* Apply TX gain settings */
+ if (tx_gain != -1)
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_3(qlm, lane),
+ c.s.pcs_sds_tx_gain = tx_gain);
+ /* Apply TX vboost settings */
+ if (tx_vboost != -1)
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_TX_CFG_3(qlm, lane),
+ c.s.cfg_tx_vboost_en = tx_vboost);
+ /* 3) Program override for the Tx coefficient request */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_0(qlm, lane),
+ if (((tx_pre != -1) && (tx_post != -1)) || (tx_swing != -1))
+ c.s.cfg_tx_coeff_req_ovrrd_val = 1;
+ if (tx_vboost != -1)
+ c.s.cfg_tx_vboost_en_ovrrd_val = 1;
+ );
+ /* 4) Enable the Tx coefficient request override enable */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ if (((tx_pre != -1) && (tx_post != -1)) || (tx_swing != -1))
+ c.s.cfg_tx_coeff_req_ovrrd_en = 1;
+ if (tx_vboost != -1)
+ c.s.cfg_tx_vboost_en_ovrrd_en = 1
+ );
+ /* 5) Issue a Control Interface Configuration Override request to start
+ the Tx equalizer Optimization cycle which applies the new Tx swing
+ and equalization settings */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.ctlifc_ovrrd_req = 1);
+
+ /* 6) Prepare for a subsequent Tx swing and Tx equalization adjustment:
+ a) Disable the Tx coefficient request override enable */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.cfg_tx_coeff_req_ovrrd_en = 0);
+ /* b) Issue a Control Interface Configuration Override request */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.ctlifc_ovrrd_req = 1);
+ /* The new Tx swing and Pre-cursor and Post-cursor settings will now take
+ effect. */
+ return 0;
+}
+
+/**
+ * Some QLM speeds need to override the default tuning parameters
+ *
+ * @param node Node to use in a Numa setup
+ * @param qlm QLM to configure
+ * @param mode Desired mode
+ * @param baud_mhz Desired speed
+ */
+void __bdk_qlm_tune(bdk_node_t node, int qlm, bdk_qlm_modes_t mode, int baud_mhz)
+{
+ /* Note: This function is not called for CCPI. For CCPI tuning, see
+ bdk-init-nz-node.c */
+ /* Tuning parameters override the KR training. Don't apply them for KR links */
+ switch (mode)
+ {
+ case BDK_QLM_MODE_10G_KR_1X1:
+ case BDK_QLM_MODE_10G_KR_2X1:
+ case BDK_QLM_MODE_10G_KR_4X1:
+ case BDK_QLM_MODE_40G_KR4_1X4:
+ return;
+ case BDK_QLM_MODE_PCIE_1X1:
+ case BDK_QLM_MODE_PCIE_2X1:
+ case BDK_QLM_MODE_PCIE_1X2:
+ case BDK_QLM_MODE_PCIE_1X4:
+ case BDK_QLM_MODE_PCIE_1X8:
+ /* Don't tune PCIe Gen3 as it has its own builtin, similar to KR */
+ if (baud_mhz > 5000)
+ return;
+ break;
+ default:
+ break;
+ }
+
+ /* We're apply tuning for all lanes on this QLM */
+ int num_lanes = bdk_qlm_get_lanes(node, qlm);
+ for (int lane = 0; lane < num_lanes; lane++)
+ {
+ /* TX Swing: First read any board specific setting from the environment */
+ int swing = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_SWING, node, qlm, lane);
+ /* If no setting, use hard coded generic defaults */
+ if (swing == -1)
+ {
+ if (baud_mhz == 6250)
+ {
+ /* Email from Brendan Metzner about RXAUI around 2/7/2016 */
+ swing = 0x12;
+ }
+ else if (baud_mhz == 10312)
+ {
+ /* From lab measurements of EBB8800 at 10.3125G */
+ swing = 0xd;
+ }
+ }
+
+ /* TX Premptap: First read any board specific setting from the environment */
+ int premptap = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_PREMPTAP, node, qlm, lane);
+ /* If no setting, use hard coded generic defaults */
+ if (premptap == -1)
+ {
+ if (baud_mhz == 6250)
+ {
+ /* From lab measurements of EBB8800 at 6.25G */
+ premptap = 0xa0;
+ }
+ else if (baud_mhz == 10312)
+ {
+ /* From lab measurements of EBB8800 at 10.3125G */
+ premptap = 0xd0;
+ }
+ }
+
+ int tx_pre = (premptap == -1) ? -1 : premptap & 0xf;
+ int tx_post = (premptap == -1) ? -1 : premptap >> 4;
+ int gain = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_GAIN, node, qlm, lane);
+ int vboost = bdk_config_get_int(BDK_CONFIG_QLM_TUNING_TX_VBOOST, node, qlm, lane);
+
+ __bdk_qlm_tune_lane_tx(node, qlm, lane, swing, tx_pre, tx_post, gain, vboost);
+
+ /* Email from Brendan Metzner about RXAUI around 2/7/2016 suggested the
+ following setting for RXAUI at 6.25G with both PHY or cable. I'm
+ applying it to all lanes running at 6.25G */
+ if (baud_mhz == 6250)
+ {
+ /* This is changing the Q/QB error sampler 0 threshold from 0xD
+ to 0xF */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_CFG_4(qlm, lane),
+ c.s.cfg_rx_errdet_ctrl = 0xcf6f);
+ }
+ }
+}
+
+/**
+ * Disables DFE for the specified QLM lane(s).
+ * This function should only be called for low-loss channels.
+ *
+ * @param node Node to configure
+ * @param qlm QLM to configure
+ * @param lane Lane to configure, or -1 for all lanes
+ */
+void __bdk_qlm_dfe_disable(int node, int qlm, int lane)
+{
+ int num_lanes = bdk_qlm_get_lanes(node, qlm);
+ int l;
+
+ for (l = 0; l < num_lanes; l++) {
+ if ((lane != -1) && (lane != l))
+ continue;
+ /* 1. Write GSERX_LANEx_RX_LOOP_CTRL = 0x0270 (var "loop_ctrl" with bits 8 & 1 cleared).
+ * bit<1> dfe_en_byp = 1'b0 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_LOOP_CTRL(qlm, l),
+ c.s.cfg_rx_lctrl = c.s.cfg_rx_lctrl & 0x3fd);
+
+ /* 2. Write GSERX_LANEx_RX_VALBBD_CTRL_1 = 0x0000 (var "ctrl1" with all bits cleared)
+ * bits<14:11> CFG_RX_DFE_C3_MVAL = 4'b0000
+ * bit<10> CFG_RX_DFE_C3_MSGN = 1'b0
+ * bits<9:6> CFG_RX_DFE_C2_MVAL = 4'b0000
+ * bit<5> CFG_RX_DFE_C2_MSGN = 1'b0
+ * bits<4:1> CFG_RX_DFE_C1_MVAL = 5'b0000
+ * bits<0> CFG_RX_DFE_C1_MSGN = 1'b0 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_1(qlm, l),
+ c.s.dfe_c3_mval = 0;
+ c.s.dfe_c3_msgn = 0;
+ c.s.dfe_c2_mval = 0;
+ c.s.dfe_c2_msgn = 0;
+ c.s.dfe_c1_mval = 0;
+ c.s.dfe_c1_msgn = 0);
+
+ /* 3. Write GSERX_LANEx_RX_VALBBD_CTRL_0 = 0x2400 (var "ctrl0" with following bits set/cleared)
+ * bits<11:10> CFG_RX_DFE_GAIN = 0x1
+ * bits<9:6> CFG_RX_DFE_C5_MVAL = 4'b0000
+ * bit<5> CFG_RX_DFE_C5_MSGN = 1'b0
+ * bits<4:1> CFG_RX_DFE_C4_MVAL = 4'b0000
+ * bit<0> CFG_RX_DFE_C4_MSGN = 1'b0 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_0(qlm, l),
+ c.s.dfe_gain = 0x1;
+ c.s.dfe_c5_mval = 0;
+ c.s.dfe_c5_msgn = 0;
+ c.s.dfe_c4_mval = 0;
+ c.s.dfe_c4_msgn = 0);
+
+ /* 4. Write GSER(0..13)_LANE(0..3)_RX_VALBBD_CTRL_2 = 0x003F //enable DFE tap overrides
+ * bit<5> dfe_ovrd_en = 1
+ * bit<4> dfe_c5_ovrd_val = 1
+ * bit<3> dfe_c4_ovrd_val = 1
+ * bit<2> dfe_c3_ovrd_val = 1
+ * bit<1> dfe_c2_ovrd_val = 1
+ * bit<0> dfe_c1_ovrd_val = 1
+ */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_2(qlm, l),
+ c.s.dfe_ovrd_en = 0x1;
+ c.s.dfe_c5_ovrd_val = 0x1;
+ c.s.dfe_c4_ovrd_val = 0x1;
+ c.s.dfe_c3_ovrd_val = 0x1;
+ c.s.dfe_c2_ovrd_val = 0x1;
+ c.s.dfe_c1_ovrd_val = 0x1);
+
+ }
+}
+
+/**
+ * Check if a specific lane is using KR training. This is used by low level GSER
+ * code to remember which QLMs and lanes need to support KR training for BGX. The
+ * hardware doesn't have a bit set aside to record this, so we repurpose the
+ * register GSERX_SCRATCH.
+ *
+ * @param node Node to check
+ * @param qlm QLM to check
+ * @param lane Lane to check
+ *
+ * @return True if this lane uses KR with BGX, false otherwise
+ */
+bool __bdk_qlm_is_lane_kr(bdk_node_t node, int qlm, int lane)
+{
+ uint64_t mask = BDK_CSR_READ(node, BDK_GSERX_SCRATCH(qlm));
+ return 1 & (mask >> lane);
+}
+
+/**
+ * Set if a specific lane is using KR training. This is used by low level GSER
+ * code to remember which QLMs and lanes need to support KR training for BGX. The
+ * hardware doesn't have a bit set aside to record this, so we repurpose the
+ * register GSERX_SCRATCH.
+ *
+ * @param node Node to set
+ * @param qlm QLM to set
+ * @param lane Lane to set
+ * @param is_kr KR (true) or XFI/XLAUI (false)
+ */
+void __bdk_qlm_set_lane_kr(bdk_node_t node, int qlm, int lane, bool is_kr)
+{
+ uint64_t mask = BDK_CSR_READ(node, BDK_GSERX_SCRATCH(qlm));
+ if (is_kr)
+ mask |= 1 << lane;
+ else
+ mask &= ~(1 << lane);
+ BDK_CSR_WRITE(node, BDK_GSERX_SCRATCH(qlm), mask);
+}
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.c b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.c
new file mode 100644
index 0000000000..a7602de758
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.c
@@ -0,0 +1,398 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 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**************************************/
+#include <bdk.h>
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-arch/bdk-csrs-rst.h"
+#include "libbdk-hal/qlm/bdk-qlm-errata-cn8xxx.h"
+
+/**
+ * Delay for the specified microseconds. When this code runs on secondary nodes
+ * before full init, the normal bdk-clock functions do not work. This function
+ * serves as a replacement that runs everywhere.
+ *
+ * @param usec Microseconds to wait
+ */
+static void wait_usec(bdk_node_t node, uint64_t usec)
+{
+ const uint64_t REF_CLOCK = 50000000; /* This is currently defined to be 50Mhz */
+ uint64_t refclock = BDK_CSR_READ(node, BDK_RST_REF_CNTR);
+ uint64_t timeout = refclock + REF_CLOCK * usec / 1000000;
+ while (refclock < timeout)
+ {
+ refclock = BDK_CSR_READ(node, BDK_RST_REF_CNTR);
+ }
+}
+
+/**
+ * Errata GSER-25992 - RX EQ Default Settings Update<p>
+ * For all GSER and all lanes when not PCIe EP:
+ * set GSER()_LANE()_RX_CFG_4[CFG_RX_ERRDET_CTRL<13:8>] = 13 (decimal)
+ * set GSER()_LANE()_RX_CTLE_CTRL[PCS_SDS_RX_CTLE_BIAS_CTRL] = 3
+ * Applied when SERDES are configured for 8G and 10G.<p>
+ * Applies to:
+ * CN88XX pass 1.x
+ * Fixed in hardware:
+ * CN88XX pass 2.x
+ * CN81XX
+ * CN83XX
+ *
+ * @param node Node to apply errata fix for
+ * @param qlm QLM to apply errata fix to
+ * @param baud_mhz QLM speed in Mhz
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_errata_gser_25992(bdk_node_t node, int qlm, int baud_mhz)
+{
+ if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+ return 0;
+ if (baud_mhz < 8000)
+ return 0;
+
+ int num_lanes = 4; /* Only applies to CN88XX, where always 4 lanes */
+ for (int lane = 0; lane < num_lanes; lane++)
+ {
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_CTLE_CTRL(qlm, lane),
+ c.s.pcs_sds_rx_ctle_bias_ctrl = 3);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_CFG_4(qlm, lane),
+ c.s.cfg_rx_errdet_ctrl = 0xcd6f);
+ }
+ return 0;
+}
+
+/**
+ * (GSER-26150) 10G PHY PLL Temperature Failure
+ *
+ * 10 Gb temperature excursions can cause lock failure. Change
+ * the calibration point of the VCO at start up to shift some
+ * available range of the VCO from -deltaT direction to the
+ * +deltaT ramp direction allowing a greater range of VCO
+ * temperatures before experiencing the failure.
+ *
+ * Applies to:
+ * CN88XX pass 1.x
+ * Fix in hardware:
+ * CN88XX pass 2.x
+ * CN81XX
+ * CN83XX
+ *
+ * Only applies to QLMs running 8G and 10G
+ *
+ * @param node Node to apply errata to
+ * @param qlm QLM to apply errata fix to
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_errata_gser_26150(bdk_node_t node, int qlm, int baud_mhz)
+{
+ if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+ return 0;
+ if (baud_mhz < 8000)
+ return 0;
+
+ int num_lanes = 4; /* Only applies to CN88XX, where always 4 lanes */
+
+ BDK_CSR_INIT(gserx_cfg, node, BDK_GSERX_CFG(qlm));
+ if (gserx_cfg.s.pcie)
+ {
+ /* Update PLL parameters */
+ /* Step 1: Set GSER()_GLBL_PLL_CFG_3[PLL_VCTRL_SEL_LCVCO_VAL] = 0x2, and
+ GSER()_GLBL_PLL_CFG_3[PCS_SDS_PLL_VCO_AMP] = 0 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_GLBL_PLL_CFG_3(qlm),
+ c.s.pll_vctrl_sel_lcvco_val = 0x2;
+ c.s.pcs_sds_pll_vco_amp = 0);
+ /* Step 2: Set GSER()_GLBL_MISC_CONFIG_1[PCS_SDS_TRIM_CHP_REG] = 0x2. */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_GLBL_MISC_CONFIG_1(qlm),
+ c.s.pcs_sds_trim_chp_reg = 0x2);
+ return 0;
+ }
+
+ /* Applying this errata twice causes problems */
+ BDK_CSR_INIT(pll_cfg_3, node, BDK_GSERX_GLBL_PLL_CFG_3(qlm));
+ if (pll_cfg_3.s.pll_vctrl_sel_lcvco_val == 0x2)
+ return 0;
+
+ /* Put PHY in P2 Power-down state Need to Power down all lanes in a
+ QLM/DLM to force PHY to P2 state */
+ for (int i=0; i<num_lanes; i++)
+ {
+ /* Step 1: Set GSER()_LANE(lane_n)_PCS_CTLIFC_0[CFG_TX_PSTATE_REQ_OVERRD_VAL] = 0x3
+ Select P2 power state for Tx lane */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_0(qlm, i),
+ c.s.cfg_tx_pstate_req_ovrrd_val = 0x3);
+ /* Step 2: Set GSER()_LANE(lane_n)_PCS_CTLIFC_1[CFG_RX_PSTATE_REQ_OVERRD_VAL] = 0x3
+ Select P2 power state for Rx lane */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_1(qlm, i),
+ c.s.cfg_rx_pstate_req_ovrrd_val = 0x3);
+ /* Step 3: Set GSER()_LANE(lane_n)_PCS_CTLIFC_2[CFG_TX_PSTATE_REQ_OVRRD_EN] = 1
+ Enable Tx power state override and Set
+ GSER()_LANE(lane_n)_PCS_CTLIFC_2[CFG_RX_PSTATE_REQ_OVRRD_EN] = 1
+ Enable Rx power state override */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, i),
+ c.s.cfg_tx_pstate_req_ovrrd_en = 0x1;
+ c.s.cfg_rx_pstate_req_ovrrd_en = 0X1);
+ /* Step 4: Set GSER()_LANE(lane_n)_PCS_CTLIFC_2[CTLIFC_OVRRD_REQ] = 1
+ Start the CTLIFC override state machine */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, i),
+ c.s.ctlifc_ovrrd_req = 0x1);
+ }
+
+ /* Update PLL parameters */
+ /* Step 5: Set GSER()_GLBL_PLL_CFG_3[PLL_VCTRL_SEL_LCVCO_VAL] = 0x2, and
+ GSER()_GLBL_PLL_CFG_3[PCS_SDS_PLL_VCO_AMP] = 0 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_GLBL_PLL_CFG_3(qlm),
+ c.s.pll_vctrl_sel_lcvco_val = 0x2;
+ c.s.pcs_sds_pll_vco_amp = 0);
+ /* Step 6: Set GSER()_GLBL_MISC_CONFIG_1[PCS_SDS_TRIM_CHP_REG] = 0x2. */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_GLBL_MISC_CONFIG_1(qlm),
+ c.s.pcs_sds_trim_chp_reg = 0x2);
+ /* Wake up PHY and transition to P0 Power-up state to bring-up the lanes,
+ need to wake up all PHY lanes */
+ for (int i=0; i<num_lanes; i++)
+ {
+ /* Step 7: Set GSER()_LANE(lane_n)_PCS_CTLIFC_0[CFG_TX_PSTATE_REQ_OVERRD_VAL] = 0x0
+ Select P0 power state for Tx lane */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_0(qlm, i),
+ c.s.cfg_tx_pstate_req_ovrrd_val = 0x0);
+ /* Step 8: Set GSER()_LANE(lane_n)_PCS_CTLIFC_1[CFG_RX_PSTATE_REQ_OVERRD_VAL] = 0x0
+ Select P0 power state for Rx lane */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_1(qlm, i),
+ c.s.cfg_rx_pstate_req_ovrrd_val = 0x0);
+ /* Step 9: Set GSER()_LANE(lane_n)_PCS_CTLIFC_2[CFG_TX_PSTATE_REQ_OVRRD_EN] = 1
+ Enable Tx power state override and Set
+ GSER()_LANE(lane_n)_PCS_CTLIFC_2[CFG_RX_PSTATE_REQ_OVRRD_EN] = 1
+ Enable Rx power state override */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, i),
+ c.s.cfg_tx_pstate_req_ovrrd_en = 0x1;
+ c.s.cfg_rx_pstate_req_ovrrd_en = 0X1);
+ /* Step 10: Set GSER()_LANE(lane_n)_PCS_CTLIFC_2[CTLIFC_OVRRD_REQ] = 1
+ Start the CTLIFC override state machine */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, i),
+ c.s.ctlifc_ovrrd_req = 0x1);
+ }
+
+ /* Step 11: Wait 10 msec */
+ wait_usec(node, 10000);
+
+ /* Release Lane Tx/Rx Power state override enables. */
+ for (int i=0; i<num_lanes; i++)
+ {
+ /* Step 12: Set GSER()_LANE(lane_n)_PCS_CTLIFC_2[CFG_TX_PSTATE_REQ_OVRRD_EN] = 0
+ Disable Tx power state override and Set
+ GSER()_LANE(lane_n)_PCS_CTLIFC_2[CFG_RX_PSTATE_REQ_OVRRD_EN] = 0
+ Disable Rx power state override */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, i),
+ c.s.cfg_tx_pstate_req_ovrrd_en = 0x0;
+ c.s.cfg_rx_pstate_req_ovrrd_en = 0X0);
+ }
+ /* Step 13: Poll GSER()_PLL_STAT.[PLL_LOCK] = 1
+ Poll and check that PLL is locked */
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_PLL_STAT(qlm), pll_lock, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_PLL_STAT[pll_lock]\n", qlm);
+ return -1;
+ }
+
+ /* Step 14: Poll GSER()_QLM_STAT.[RST_RDY] = 1
+ Poll and check that QLM/DLM is Ready */
+ if (BDK_CSR_WAIT_FOR_FIELD(node, BDK_GSERX_QLM_STAT(qlm), rst_rdy, ==, 1, 10000))
+ {
+ bdk_error("QLM%d: Timeout waiting for GSERX_QLM_STAT[rst_rdy]\n", qlm);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Errata (GSER-26636) 10G-KR/40G-KR - Inverted Tx Coefficient Direction Change
+ * Applied to all 10G standards (required for KR) but also applied to other
+ * standards in case software training is used.
+ * Applies to:
+ * CN88XX pass 1.x
+ * Fixed in hardware:
+ * CN88XX pass 2.x
+ * CN81XX
+ * CN83XX
+ *
+ * @param node Node to apply errata fix for
+ * @param qlm QLM to apply errata fix to
+ * @param baud_mhz QLM speed in Mhz
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_errata_gser_26636(bdk_node_t node, int qlm, int baud_mhz)
+{
+ if (!CAVIUM_IS_MODEL(CAVIUM_CN88XX_PASS1_X))
+ return 0;
+
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_RX_TXDIR_CTRL_1(qlm),
+ c.s.rx_precorr_chg_dir = 1;
+ c.s.rx_tap1_chg_dir = 1);
+ return 0;
+}
+
+/**
+ * (GSER-27140) SERDES has temperature drift sensitivity in the RX EQ<p>
+ * SERDES temperature drift sensitivity in receiver. Issues have
+ * been found with the Bit Error Rate (BER) reliability of
+ * 10GBASE-KR links over the commercial temperature range (0 to 100C),
+ * especially when subjected to rapid thermal ramp stress testing.
+ * (See HRM for corresponding case temperature requirements for each speed grade.)<p>
+ * Applies to:
+ * CN88XX pass 1.x
+ * CN88XX pass 2.x
+ * CN83XX pass 1.x
+ * CN81XX pass 1.x
+ * Fixed in hardware:
+ * TBD<p>
+ * Only applies to QLMs running 10G
+ *
+ * @param node Note to apply errata fix to
+ * @param qlm QLM to apply errata fix to
+ * @param baud_mhz QLM baud rate in Mhz
+ * @param channel_loss
+ * Insertion loss at Nyquist rate (e.g. 5.125Ghz for XFI/XLAUI) in dB
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_errata_gser_27140(bdk_node_t node, int qlm, int baud_mhz, int channel_loss)
+{
+ if (baud_mhz != 10312)
+ return 0;
+
+ /* A channel loss of -1 means the loss is unknown. A short channel is
+ considered to have loss between 0 and 10 dB */
+ bool short_channel = (channel_loss >= 0) && (channel_loss <= 10);
+
+ /* I. For each GSER QLM: */
+ /* Workaround GSER-27140: */
+ /* (1) GSER-26150 = Applied by the caller */
+ /* (2) Write GSER()_LANE_VMA_FINE_CTRL_0[RX_SDLL_IQ_MAX_FINE] = 0xE */
+ /* (3) Write GSER()_LANE_VMA_FINE_CTRL_0[RX_SDLL_IQ_MIN_FINE] = 0x8 */
+ /* (4) Write GSER()_LANE_VMA_FINE_CTRL_0[RX_SDLL_IQ_STEP_FINE] = 0x2 */
+ /* (5) Write GSER()_LANE_VMA_FINE_CTRL_0[VMA_WINDOW_WAIT_FINE] = 0x5 */
+ /* (6) Write GSER()_LANE_VMA_FINE_CTRL_0[LMS_WAIT_TIME_FINE] = 0x5 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_VMA_FINE_CTRL_0(qlm),
+ c.s.rx_sdll_iq_max_fine = 0xE;
+ c.s.rx_sdll_iq_min_fine = 0x8;
+ c.s.rx_sdll_iq_step_fine = 0x2;
+ c.s.vma_window_wait_fine = 0x5;
+ c.s.lms_wait_time_fine = 0x5);
+ /* (7) Write GSER()_LANE_VMA_FINE_CTRL_2[RX_PRECTLE_GAIN_MAX_FINE] = 0xB */
+ /* (8) Write GSER()_LANE_VMA_FINE_CTRL_2[RX_PRECTLE_GAIN_MIN_FINE] = 0x6(long) or 0x0(short) */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANE_VMA_FINE_CTRL_2(qlm),
+ c.s.rx_prectle_gain_max_fine = 0xB;
+ c.s.rx_prectle_gain_min_fine = short_channel ? 0x0 : 0x6);
+ /* (9) Write GSER()_RX_TXDIR_CTRL_0[RX_BOOST_LO_THRES] = 0x4 */
+ /* (10) Write GSER()_RX_TXDIR_CTRL_0[RX_BOOST_HI_THRES] = 0xB */
+ /* (11) Write GSER()_RX_TXDIR_CTRL_0[RX_BOOST_HI_VAL] = 0xF */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_RX_TXDIR_CTRL_0(qlm),
+ c.s.rx_boost_lo_thrs = 0x4;
+ c.s.rx_boost_hi_thrs = 0xB;
+ c.s.rx_boost_hi_val = 0xF);
+ /* (12) Write GSER()_RX_TXDIR_CTRL_1[RX_TAP1_LO_THRS] = 0x8 */
+ /* (13) Write GSER()_RX_TXDIR_CTRL_1[RX_TAP1_HI_THRS] = 0x17 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_RX_TXDIR_CTRL_1(qlm),
+ c.s.rx_tap1_lo_thrs = 0x8;
+ c.s.rx_tap1_hi_thrs = 0x17);
+
+ /* (14) Write GSER()_EQ_WAIT_TIME[RXEQ_WAIT_CNT] = 0x6 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_EQ_WAIT_TIME(qlm),
+ c.s.rxeq_wait_cnt = 0x6);
+ /* (15) Write GSER()_RX_TXDIR_CTRL_2[RX_PRECORR_HI_THRS] = 0xC0 */
+ /* (16) Write GSER()_RX_TXDIR_CTRL_2[RX_PRECORR_LO_THRS] = 0x40 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_RX_TXDIR_CTRL_2(qlm),
+ c.s.rx_precorr_hi_thrs = 0xc0;
+ c.s.rx_precorr_lo_thrs = 0x40);
+
+ /* We can't call the normal bdk-qlm function as it uses pointers that
+ don't work when running in secondary nodes before CCPI is up */
+ int num_lanes = 4;
+ if (CAVIUM_IS_MODEL(CAVIUM_CN81XX) || (CAVIUM_IS_MODEL(CAVIUM_CN83XX) && (qlm >= 4)))
+ num_lanes = 2;
+
+ /* II. For each GSER QLM SerDes lane: */
+ /* Establish typical values, which are already reset values in pass 2: */
+ for (int lane = 0; lane < num_lanes; lane++)
+ {
+ /* (17) For each GSER lane in the 10GBASE-KR link: */
+ /* (a) Write GSER()_LANE()_RX_VALBBD_CTRL_0[AGC_GAIN] = 0x3 */
+ /* (b) Write GSER()_LANE()_RX_VALBBD_CTRL_0[DFE_GAIN] = 0x2 */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_VALBBD_CTRL_0(qlm, lane),
+ c.s.agc_gain = 0x3;
+ c.s.dfe_gain = 0x2);
+ }
+
+ /* III. The GSER QLM SerDes Lanes are now ready. */
+ return 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
+ * Applies to:
+ * CN88XX pass 1.x, 2.0, 2.1
+ * Fixed in hardware:
+ * CN88XX pass 2.2 and higher
+ * CN81XX
+ * CN83XX
+ *
+ * @param node Node to apply errata fix for
+ * @param qlm QLM to apply errata fix to
+ * @param lane
+ *
+ * @return Zero on success, negative on failure
+ */
+int __bdk_qlm_errata_gser_27882(bdk_node_t node, int qlm, int lane)
+{
+ /* Toggle Toggle Tx Coeff Req override to force an update */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_0(qlm, lane),
+ c.s.cfg_tx_coeff_req_ovrrd_val = 1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.cfg_tx_coeff_req_ovrrd_en = 1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.ctlifc_ovrrd_req = 1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.cfg_tx_coeff_req_ovrrd_en = 0);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PCS_CTLIFC_2(qlm, lane),
+ c.s.ctlifc_ovrrd_req = 1);
+ return 0;
+}
+
diff --git a/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-margin-cn8xxx.c b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-margin-cn8xxx.c
new file mode 100644
index 0000000000..c970f2189e
--- /dev/null
+++ b/src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-margin-cn8xxx.c
@@ -0,0 +1,271 @@
+/***********************license start***********************************
+* Copyright (c) 2003-2017 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**************************************/
+#include <bdk.h>
+#include "libbdk-arch/bdk-csrs-gser.h"
+#include "libbdk-hal/if/bdk-if.h"
+#include "libbdk-hal/bdk-qlm.h"
+#include "libbdk-hal/bdk-utils.h"
+
+/* This code is an optional part of the BDK. It is only linked in
+ if BDK_REQUIRE() needs it */
+BDK_REQUIRE_DEFINE(QLM_MARGIN);
+
+typedef union
+{
+ struct
+ {
+ uint64_t rx_os_mvalbbd_2 :16;
+ uint64_t rx_os_mvalbbd_1 :16;
+ uint64_t reserved_63_32 :32;
+
+ } s;
+ struct
+ {
+ uint64_t Qb :6;
+ uint64_t Q :6;
+ uint64_t Lb :6; // Spans the two registers
+ uint64_t L :6;
+ uint64_t qerr0 :6;
+ int64_t reserved_63_30 :34;
+ } f;
+ uint64_t u;
+} rx_os_mvalbbd_t;
+
+int __bdk_disable_ccpi_error_report = 0;
+
+static int convert_to_signed_mag(int source)
+{
+ /* Synopsis encoded sign in an unexpected way. 0=negative and 1=positive
+ So bit 5 should be 0 for negative numbers, 1 for positive numbers */
+ if (source < 0)
+ source = -source;
+ else
+ source |= 0x20;
+ return source;
+}
+
+static rx_os_mvalbbd_t get_current_settings(bdk_node_t node, int qlm, int qlm_lane)
+{
+ rx_os_mvalbbd_t mvalbbd;
+ mvalbbd.u = 0;
+
+ BDK_CSR_INIT(rx_cfg_1, node, BDK_GSERX_LANEX_RX_CFG_1(qlm, qlm_lane));
+ if (!rx_cfg_1.s.pcs_sds_rx_os_men)
+ {
+ /* Get the current settings */
+ BDK_CSR_INIT(rx_os_out_1, node, BDK_GSERX_LANEX_RX_OS_OUT_1(qlm, qlm_lane));
+ BDK_CSR_INIT(rx_os_out_2, node, BDK_GSERX_LANEX_RX_OS_OUT_2(qlm, qlm_lane));
+ BDK_CSR_INIT(rx_os_out_3, node, BDK_GSERX_LANEX_RX_OS_OUT_3(qlm, qlm_lane));
+ int qerr0 = bdk_extracts(rx_os_out_1.u, 0, 6);
+ int lb = bdk_extracts(rx_os_out_2.u, 0, 6);
+ int l = bdk_extracts(rx_os_out_2.u, 6, 6);
+ int qb = bdk_extracts(rx_os_out_3.u, 0, 6);
+ int q = bdk_extracts(rx_os_out_3.u, 6, 6);
+ /* Enable the override with the current values */
+ mvalbbd.f.Qb = convert_to_signed_mag(qb);
+ mvalbbd.f.Q = convert_to_signed_mag(q);
+ mvalbbd.f.Lb = convert_to_signed_mag(lb);
+ mvalbbd.f.L = convert_to_signed_mag(l);
+ mvalbbd.f.qerr0 = convert_to_signed_mag(qerr0);
+ }
+ else
+ {
+ BDK_CSR_INIT(mvalbbd_1, node, BDK_GSERX_LANEX_RX_OS_MVALBBD_1(qlm, qlm_lane));
+ mvalbbd.s.rx_os_mvalbbd_1 = mvalbbd_1.s.pcs_sds_rx_os_mval;
+ BDK_CSR_INIT(mvalbbd_2, node, BDK_GSERX_LANEX_RX_OS_MVALBBD_2(qlm, qlm_lane));
+ mvalbbd.s.rx_os_mvalbbd_2 = mvalbbd_2.s.pcs_sds_rx_os_mval;
+ }
+ //printf("qerr0=%d, lb=%d, l=%d, qb=%d, q=%d\n",
+ // mvalbbd.f.qerr0, mvalbbd.f.Lb, mvalbbd.f.L, mvalbbd.f.Qb, mvalbbd.f.Q);
+ return mvalbbd;
+}
+
+/**
+ * Get the current RX margining parameter
+ *
+ * @param node Node to read margin value from
+ * @param qlm QLM to read from
+ * @param qlm_lane Lane to read
+ * @param margin_type
+ * Type of margining parameter to read
+ *
+ * @return Current margining parameter value
+ */
+int64_t bdk_qlm_margin_rx_get(bdk_node_t node, int qlm, int qlm_lane, bdk_qlm_margin_t margin_type)
+{
+ rx_os_mvalbbd_t mvalbbd = get_current_settings(node, qlm, qlm_lane);
+
+ switch (margin_type)
+ {
+ case BDK_QLM_MARGIN_VERTICAL:
+ if (mvalbbd.f.Q & 0x20) /* Check if sign bit says positive */
+ return mvalbbd.f.Q & 0x1f; /* positive, strip off sign */
+ else
+ return -mvalbbd.f.Q; /* negative */
+ case BDK_QLM_MARGIN_HORIZONTAL:
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Get the current RX margining parameter minimum value
+ *
+ * @param node Node to read margin value from
+ * @param qlm QLM to read from
+ * @param qlm_lane Lane to read
+ * @param margin_type
+ * Type of margining parameter to read
+ *
+ * @return Current margining parameter minimum value
+ */
+int64_t bdk_qlm_margin_rx_get_min(bdk_node_t node, int qlm, int qlm_lane, bdk_qlm_margin_t margin_type)
+{
+ switch (margin_type)
+ {
+ case BDK_QLM_MARGIN_VERTICAL:
+ return -31;
+ case BDK_QLM_MARGIN_HORIZONTAL:
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Get the current RX margining parameter maximum value
+ *
+ * @param node Node to read margin value from
+ * @param qlm QLM to read from
+ * @param qlm_lane Lane to read
+ * @param margin_type
+ * Type of margining parameter to read
+ *
+ * @return Current margining parameter maximum value
+ */
+int64_t bdk_qlm_margin_rx_get_max(bdk_node_t node, int qlm, int qlm_lane, bdk_qlm_margin_t margin_type)
+{
+ switch (margin_type)
+ {
+ case BDK_QLM_MARGIN_VERTICAL:
+ return 31;
+ case BDK_QLM_MARGIN_HORIZONTAL:
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Set the current RX margining parameter value
+ *
+ * @param node Node to set margin value on
+ * @param qlm QLM to set
+ * @param qlm_lane Lane to set
+ * @param margin_type
+ * Type of margining parameter to set
+ * @param value Value of margining parameter
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_margin_rx_set(bdk_node_t node, int qlm, int qlm_lane, bdk_qlm_margin_t margin_type, int value)
+{
+ rx_os_mvalbbd_t mvalbbd = get_current_settings(node, qlm, qlm_lane);
+
+ switch (margin_type)
+ {
+ case BDK_QLM_MARGIN_VERTICAL:
+ if (value < 0)
+ mvalbbd.f.Q = -value; /* Sign bit is zero, weird Synopsys */
+ else
+ mvalbbd.f.Q = value | 0x20; /* Sign bit is one, weird Synopsys */
+ break;
+ case BDK_QLM_MARGIN_HORIZONTAL:
+ return -1;
+ }
+
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_OS_MVALBBD_1(qlm, qlm_lane),
+ c.s.pcs_sds_rx_os_mval = mvalbbd.s.rx_os_mvalbbd_1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_OS_MVALBBD_2(qlm, qlm_lane),
+ c.s.pcs_sds_rx_os_mval = mvalbbd.s.rx_os_mvalbbd_2);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_CFG_1(qlm, qlm_lane),
+ c.s.pcs_sds_rx_os_men = 1);
+
+ /* Disable the DFE(s), gives a better eye measurement */
+ BDK_CSR_INIT(pwr_ctrl, node, BDK_GSERX_LANEX_PWR_CTRL(qlm, qlm_lane));
+ if (!pwr_ctrl.s.rx_lctrl_ovrrd_en)
+ {
+ BDK_CSR_WRITE(node, BDK_GSERX_LANEX_RX_LOOP_CTRL(qlm, qlm_lane), 0xF1);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PWR_CTRL(qlm, qlm_lane),
+ c.s.rx_lctrl_ovrrd_en = 1);
+ }
+
+ if (qlm >= 8)
+ __bdk_disable_ccpi_error_report = 1;
+
+ return 0;
+}
+
+/**
+ * Restore the supplied RX margining parameter value as if it was never set. This
+ * disables any overrides in the SERDES need to perform margining
+ *
+ * @param node Node to restore margin value on
+ * @param qlm QLM to restore
+ * @param qlm_lane Lane to restore
+ * @param margin_type
+ * Type of margining parameter to restore
+ * @param value Value of margining parameter
+ *
+ * @return Zero on success, negative on failure
+ */
+int bdk_qlm_margin_rx_restore(bdk_node_t node, int qlm, int qlm_lane, bdk_qlm_margin_t margin_type, int value)
+{
+ BDK_CSR_INIT(rx_cfg_1, node, BDK_GSERX_LANEX_RX_CFG_1(qlm, qlm_lane));
+ /* Return if no overrides have been applied */
+ if (!rx_cfg_1.s.pcs_sds_rx_os_men)
+ return 0;
+ bdk_qlm_margin_rx_set(node, qlm, qlm_lane, margin_type, value);
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_RX_CFG_1(qlm, qlm_lane),
+ c.s.pcs_sds_rx_os_men = 0);
+ /* Enable the DFE(s) */
+ BDK_CSR_MODIFY(c, node, BDK_GSERX_LANEX_PWR_CTRL(qlm, qlm_lane),
+ c.s.rx_lctrl_ovrrd_en = 0);
+ __bdk_disable_ccpi_error_report = 0;
+ return 0;
+}
+