From 7d48ac5c7dfb52fc470bbad1013b4d460bc6a1e0 Mon Sep 17 00:00:00 2001 From: David Hendricks Date: Fri, 9 Mar 2018 14:30:38 -0800 Subject: 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 Reviewed-on: https://review.coreboot.org/25089 Reviewed-by: Philipp Deppenwiese Tested-by: build bot (Jenkins) --- .../cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c | 1003 ++++++++++++++++++++ 1 file changed, 1003 insertions(+) create mode 100644 src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c (limited to 'src/vendorcode/cavium/bdk/libbdk-hal/qlm/bdk-qlm-cn81xx.c') 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 +#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; +} -- cgit v1.2.3