// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020-2022 Marvell International Ltd. * * Helper functions for common, but complicated tasks. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @INTERNAL * This structure specifies the interface methods used by an interface. * * @param mode Interface mode. * * @param enumerate Method the get number of interface ports. * * @param probe Method to probe an interface to get the number of * connected ports. * * @param enable Method to enable an interface * * @param link_get Method to get the state of an interface link. * * @param link_set Method to configure an interface link to the specified * state. * * @param loopback Method to configure a port in loopback. */ struct iface_ops { cvmx_helper_interface_mode_t mode; int (*enumerate)(int xiface); int (*probe)(int xiface); int (*enable)(int xiface); cvmx_helper_link_info_t (*link_get)(int ipd_port); int (*link_set)(int ipd_port, cvmx_helper_link_info_t link_info); int (*loopback)(int ipd_port, int en_in, int en_ex); }; /** * @INTERNAL * This structure is used by disabled interfaces. */ static const struct iface_ops iface_ops_dis = { .mode = CVMX_HELPER_INTERFACE_MODE_DISABLED, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as gmii. */ static const struct iface_ops iface_ops_gmii = { .mode = CVMX_HELPER_INTERFACE_MODE_GMII, .enumerate = __cvmx_helper_rgmii_probe, .probe = __cvmx_helper_rgmii_probe, .enable = __cvmx_helper_rgmii_enable, .link_get = __cvmx_helper_gmii_link_get, .link_set = __cvmx_helper_rgmii_link_set, .loopback = __cvmx_helper_rgmii_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as rgmii. */ static const struct iface_ops iface_ops_rgmii = { .mode = CVMX_HELPER_INTERFACE_MODE_RGMII, .enumerate = __cvmx_helper_rgmii_probe, .probe = __cvmx_helper_rgmii_probe, .enable = __cvmx_helper_rgmii_enable, .link_get = __cvmx_helper_rgmii_link_get, .link_set = __cvmx_helper_rgmii_link_set, .loopback = __cvmx_helper_rgmii_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as sgmii that use the gmx mac. */ static const struct iface_ops iface_ops_sgmii = { .mode = CVMX_HELPER_INTERFACE_MODE_SGMII, .enumerate = __cvmx_helper_sgmii_enumerate, .probe = __cvmx_helper_sgmii_probe, .enable = __cvmx_helper_sgmii_enable, .link_get = __cvmx_helper_sgmii_link_get, .link_set = __cvmx_helper_sgmii_link_set, .loopback = __cvmx_helper_sgmii_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as sgmii that use the bgx mac. */ static const struct iface_ops iface_ops_bgx_sgmii = { .mode = CVMX_HELPER_INTERFACE_MODE_SGMII, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_sgmii_enable, .link_get = __cvmx_helper_bgx_sgmii_link_get, .link_set = __cvmx_helper_bgx_sgmii_link_set, .loopback = __cvmx_helper_bgx_sgmii_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as qsgmii. */ static const struct iface_ops iface_ops_qsgmii = { .mode = CVMX_HELPER_INTERFACE_MODE_QSGMII, .enumerate = __cvmx_helper_sgmii_enumerate, .probe = __cvmx_helper_sgmii_probe, .enable = __cvmx_helper_sgmii_enable, .link_get = __cvmx_helper_sgmii_link_get, .link_set = __cvmx_helper_sgmii_link_set, .loopback = __cvmx_helper_sgmii_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as xaui using the gmx mac. */ static const struct iface_ops iface_ops_xaui = { .mode = CVMX_HELPER_INTERFACE_MODE_XAUI, .enumerate = __cvmx_helper_xaui_enumerate, .probe = __cvmx_helper_xaui_probe, .enable = __cvmx_helper_xaui_enable, .link_get = __cvmx_helper_xaui_link_get, .link_set = __cvmx_helper_xaui_link_set, .loopback = __cvmx_helper_xaui_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as xaui using the gmx mac. */ static const struct iface_ops iface_ops_bgx_xaui = { .mode = CVMX_HELPER_INTERFACE_MODE_XAUI, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_xaui_enable, .link_get = __cvmx_helper_bgx_xaui_link_get, .link_set = __cvmx_helper_bgx_xaui_link_set, .loopback = __cvmx_helper_bgx_xaui_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as rxaui. */ static const struct iface_ops iface_ops_rxaui = { .mode = CVMX_HELPER_INTERFACE_MODE_RXAUI, .enumerate = __cvmx_helper_xaui_enumerate, .probe = __cvmx_helper_xaui_probe, .enable = __cvmx_helper_xaui_enable, .link_get = __cvmx_helper_xaui_link_get, .link_set = __cvmx_helper_xaui_link_set, .loopback = __cvmx_helper_xaui_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as xaui using the gmx mac. */ static const struct iface_ops iface_ops_bgx_rxaui = { .mode = CVMX_HELPER_INTERFACE_MODE_RXAUI, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_xaui_enable, .link_get = __cvmx_helper_bgx_xaui_link_get, .link_set = __cvmx_helper_bgx_xaui_link_set, .loopback = __cvmx_helper_bgx_xaui_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as xlaui. */ static const struct iface_ops iface_ops_bgx_xlaui = { .mode = CVMX_HELPER_INTERFACE_MODE_XLAUI, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_xaui_enable, .link_get = __cvmx_helper_bgx_xaui_link_get, .link_set = __cvmx_helper_bgx_xaui_link_set, .loopback = __cvmx_helper_bgx_xaui_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as xfi. */ static const struct iface_ops iface_ops_bgx_xfi = { .mode = CVMX_HELPER_INTERFACE_MODE_XFI, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_xaui_enable, .link_get = __cvmx_helper_bgx_xaui_link_get, .link_set = __cvmx_helper_bgx_xaui_link_set, .loopback = __cvmx_helper_bgx_xaui_configure_loopback, }; static const struct iface_ops iface_ops_bgx_10G_KR = { .mode = CVMX_HELPER_INTERFACE_MODE_10G_KR, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_xaui_enable, .link_get = __cvmx_helper_bgx_xaui_link_get, .link_set = __cvmx_helper_bgx_xaui_link_set, .loopback = __cvmx_helper_bgx_xaui_configure_loopback, }; static const struct iface_ops iface_ops_bgx_40G_KR4 = { .mode = CVMX_HELPER_INTERFACE_MODE_40G_KR4, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_xaui_enable, .link_get = __cvmx_helper_bgx_xaui_link_get, .link_set = __cvmx_helper_bgx_xaui_link_set, .loopback = __cvmx_helper_bgx_xaui_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as ilk. */ static const struct iface_ops iface_ops_ilk = { .mode = CVMX_HELPER_INTERFACE_MODE_ILK, .enumerate = __cvmx_helper_ilk_enumerate, .probe = __cvmx_helper_ilk_probe, .enable = __cvmx_helper_ilk_enable, .link_get = __cvmx_helper_ilk_link_get, .link_set = __cvmx_helper_ilk_link_set, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as npi. */ static const struct iface_ops iface_ops_npi = { .mode = CVMX_HELPER_INTERFACE_MODE_NPI, .enumerate = __cvmx_helper_npi_probe, .probe = __cvmx_helper_npi_probe, .enable = __cvmx_helper_npi_enable, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as agl. */ static const struct iface_ops iface_ops_agl = { .mode = CVMX_HELPER_INTERFACE_MODE_AGL, .enumerate = __cvmx_helper_agl_enumerate, .probe = __cvmx_helper_agl_probe, .enable = __cvmx_helper_agl_enable, .link_get = __cvmx_helper_agl_link_get, .link_set = __cvmx_helper_agl_link_set, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as mixed mode, some ports are sgmii and some are xfi. */ static const struct iface_ops iface_ops_bgx_mixed = { .mode = CVMX_HELPER_INTERFACE_MODE_MIXED, .enumerate = __cvmx_helper_bgx_enumerate, .probe = __cvmx_helper_bgx_probe, .enable = __cvmx_helper_bgx_mixed_enable, .link_get = __cvmx_helper_bgx_mixed_link_get, .link_set = __cvmx_helper_bgx_mixed_link_set, .loopback = __cvmx_helper_bgx_mixed_configure_loopback, }; /** * @INTERNAL * This structure specifies the interface methods used by interfaces * configured as loop. */ static const struct iface_ops iface_ops_loop = { .mode = CVMX_HELPER_INTERFACE_MODE_LOOP, .enumerate = __cvmx_helper_loop_enumerate, .probe = __cvmx_helper_loop_probe, }; const struct iface_ops *iface_node_ops[CVMX_MAX_NODES][CVMX_HELPER_MAX_IFACE]; #define iface_ops iface_node_ops[0] struct cvmx_iface { int cvif_ipd_nports; int cvif_has_fcs; /* PKO fcs for this interface. */ enum cvmx_pko_padding cvif_padding; cvmx_helper_link_info_t *cvif_ipd_port_link_info; }; /* * This has to be static as u-boot expects to probe an interface and * gets the number of its ports. */ static struct cvmx_iface cvmx_interfaces[CVMX_MAX_NODES][CVMX_HELPER_MAX_IFACE]; int __cvmx_helper_get_num_ipd_ports(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); struct cvmx_iface *piface; if (xi.interface >= cvmx_helper_get_number_of_interfaces()) return -1; piface = &cvmx_interfaces[xi.node][xi.interface]; return piface->cvif_ipd_nports; } enum cvmx_pko_padding __cvmx_helper_get_pko_padding(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); struct cvmx_iface *piface; if (xi.interface >= cvmx_helper_get_number_of_interfaces()) return CVMX_PKO_PADDING_NONE; piface = &cvmx_interfaces[xi.node][xi.interface]; return piface->cvif_padding; } int __cvmx_helper_init_interface(int xiface, int num_ipd_ports, int has_fcs, enum cvmx_pko_padding pad) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); struct cvmx_iface *piface; cvmx_helper_link_info_t *p; int i; int sz; u64 addr; char name[32]; if (xi.interface >= cvmx_helper_get_number_of_interfaces()) return -1; piface = &cvmx_interfaces[xi.node][xi.interface]; piface->cvif_ipd_nports = num_ipd_ports; piface->cvif_padding = pad; piface->cvif_has_fcs = has_fcs; /* * allocate the per-ipd_port link_info structure */ sz = piface->cvif_ipd_nports * sizeof(cvmx_helper_link_info_t); snprintf(name, sizeof(name), "__int_%d_link_info", xi.interface); addr = CAST64(cvmx_bootmem_alloc_named_range_once(sz, 0, 0, __alignof(cvmx_helper_link_info_t), name, NULL)); piface->cvif_ipd_port_link_info = (cvmx_helper_link_info_t *)__cvmx_phys_addr_to_ptr(addr, sz); if (!piface->cvif_ipd_port_link_info) { if (sz != 0) debug("iface %d failed to alloc link info\n", xi.interface); return -1; } /* Initialize them */ p = piface->cvif_ipd_port_link_info; for (i = 0; i < piface->cvif_ipd_nports; i++) { (*p).u64 = 0; p++; } return 0; } int __cvmx_helper_set_link_info(int xiface, int index, cvmx_helper_link_info_t link_info) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); struct cvmx_iface *piface; if (xi.interface >= cvmx_helper_get_number_of_interfaces()) return -1; piface = &cvmx_interfaces[xi.node][xi.interface]; if (piface->cvif_ipd_port_link_info) { piface->cvif_ipd_port_link_info[index] = link_info; return 0; } return -1; } cvmx_helper_link_info_t __cvmx_helper_get_link_info(int xiface, int port) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); struct cvmx_iface *piface; cvmx_helper_link_info_t err; err.u64 = 0; if (xi.interface >= cvmx_helper_get_number_of_interfaces()) return err; piface = &cvmx_interfaces[xi.node][xi.interface]; if (piface->cvif_ipd_port_link_info) return piface->cvif_ipd_port_link_info[port]; return err; } /** * Returns if FCS is enabled for the specified interface and port * * @param xiface - interface to check * * Return: zero if FCS is not used, otherwise FCS is used. */ int __cvmx_helper_get_has_fcs(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); return cvmx_interfaces[xi.node][xi.interface].cvif_has_fcs; } u64 cvmx_rgmii_backpressure_dis = 1; typedef int (*cvmx_export_config_t)(void); cvmx_export_config_t cvmx_export_app_config; /* * internal functions that are not exported in the .h file but must be * declared to make gcc happy. */ extern cvmx_helper_link_info_t __cvmx_helper_get_link_info(int interface, int port); /** * cvmx_override_ipd_port_setup(int ipd_port) is a function * pointer. It is meant to allow customization of the IPD * port/port kind setup before packet input/output comes online. * It is called after cvmx-helper does the default IPD configuration, * but before IPD is enabled. Users should set this pointer to a * function before calling any cvmx-helper operations. */ void (*cvmx_override_ipd_port_setup)(int ipd_port) = NULL; /** * Return the number of interfaces the chip has. Each interface * may have multiple ports. Most chips support two interfaces, * but the CNX0XX and CNX1XX are exceptions. These only support * one interface. * * Return: Number of interfaces on chip */ int cvmx_helper_get_number_of_interfaces(void) { if (OCTEON_IS_MODEL(OCTEON_CN68XX)) return 9; else if (OCTEON_IS_MODEL(OCTEON_CN66XX)) if (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0)) return 7; else return 8; else if (OCTEON_IS_MODEL(OCTEON_CN63XX)) return 6; else if (OCTEON_IS_MODEL(OCTEON_CN61XX) || OCTEON_IS_MODEL(OCTEON_CNF71XX)) return 4; else if (OCTEON_IS_MODEL(OCTEON_CN70XX)) return 5; else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) return 10; else if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) return 5; else if (OCTEON_IS_MODEL(OCTEON_CN73XX)) return 5; else return 3; } int __cvmx_helper_early_ports_on_interface(int interface) { int ports; if (octeon_has_feature(OCTEON_FEATURE_PKND)) return cvmx_helper_interface_enumerate(interface); ports = cvmx_helper_interface_enumerate(interface); ports = __cvmx_helper_board_interface_probe(interface, ports); return ports; } /** * Return the number of ports on an interface. Depending on the * chip and configuration, this can be 1-16. A value of 0 * specifies that the interface doesn't exist or isn't usable. * * @param xiface to get the port count for * * Return: Number of ports on interface. Can be Zero. */ int cvmx_helper_ports_on_interface(int xiface) { if (octeon_has_feature(OCTEON_FEATURE_PKND)) return cvmx_helper_interface_enumerate(xiface); else return __cvmx_helper_get_num_ipd_ports(xiface); } /** * @INTERNAL * Return interface mode for CN70XX. */ static cvmx_helper_interface_mode_t __cvmx_get_mode_cn70xx(int interface) { /* SGMII/RXAUI/QSGMII */ if (interface < 2) { enum cvmx_qlm_mode qlm_mode = cvmx_qlm_get_dlm_mode(0, interface); if (qlm_mode == CVMX_QLM_MODE_SGMII) iface_ops[interface] = &iface_ops_sgmii; else if (qlm_mode == CVMX_QLM_MODE_QSGMII) iface_ops[interface] = &iface_ops_qsgmii; else if (qlm_mode == CVMX_QLM_MODE_RXAUI) iface_ops[interface] = &iface_ops_rxaui; else iface_ops[interface] = &iface_ops_dis; } else if (interface == 2) { /* DPI */ iface_ops[interface] = &iface_ops_npi; } else if (interface == 3) { /* LOOP */ iface_ops[interface] = &iface_ops_loop; } else if (interface == 4) { /* RGMII (AGL) */ cvmx_agl_prtx_ctl_t prtx_ctl; prtx_ctl.u64 = csr_rd(CVMX_AGL_PRTX_CTL(0)); if (prtx_ctl.s.mode == 0) iface_ops[interface] = &iface_ops_agl; else iface_ops[interface] = &iface_ops_dis; } else { iface_ops[interface] = &iface_ops_dis; } return iface_ops[interface]->mode; } /** * @INTERNAL * Return interface mode for CN78XX. */ static cvmx_helper_interface_mode_t __cvmx_get_mode_cn78xx(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); /* SGMII/RXAUI/XAUI */ if (xi.interface < 6) { int qlm = cvmx_qlm_lmac(xiface, 0); enum cvmx_qlm_mode qlm_mode; if (qlm == -1) { iface_node_ops[xi.node][xi.interface] = &iface_ops_dis; return iface_node_ops[xi.node][xi.interface]->mode; } qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, qlm); if (qlm_mode == CVMX_QLM_MODE_SGMII) iface_node_ops[xi.node][xi.interface] = &iface_ops_bgx_sgmii; else if (qlm_mode == CVMX_QLM_MODE_XAUI) iface_node_ops[xi.node][xi.interface] = &iface_ops_bgx_xaui; else if (qlm_mode == CVMX_QLM_MODE_XLAUI) iface_node_ops[xi.node][xi.interface] = &iface_ops_bgx_xlaui; else if (qlm_mode == CVMX_QLM_MODE_XFI) iface_node_ops[xi.node][xi.interface] = &iface_ops_bgx_xfi; else if (qlm_mode == CVMX_QLM_MODE_RXAUI) iface_node_ops[xi.node][xi.interface] = &iface_ops_bgx_rxaui; else iface_node_ops[xi.node][xi.interface] = &iface_ops_dis; } else if (xi.interface < 8) { enum cvmx_qlm_mode qlm_mode; int found = 0; int i; int intf, lane_mask; if (xi.interface == 6) { intf = 6; lane_mask = cvmx_ilk_lane_mask[xi.node][0]; } else { intf = 7; lane_mask = cvmx_ilk_lane_mask[xi.node][1]; } switch (lane_mask) { default: case 0x0: iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xf: qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, 4); if (qlm_mode == CVMX_QLM_MODE_ILK) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xff: found = 0; for (i = 4; i < 6; i++) { qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, i); if (qlm_mode == CVMX_QLM_MODE_ILK) found++; } if (found == 2) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xfff: found = 0; for (i = 4; i < 7; i++) { qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, i); if (qlm_mode == CVMX_QLM_MODE_ILK) found++; } if (found == 3) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xff00: found = 0; for (i = 6; i < 8; i++) { qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, i); if (qlm_mode == CVMX_QLM_MODE_ILK) found++; } if (found == 2) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xf0: qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, 5); if (qlm_mode == CVMX_QLM_MODE_ILK) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xf00: qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, 6); if (qlm_mode == CVMX_QLM_MODE_ILK) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xf000: qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, 7); if (qlm_mode == CVMX_QLM_MODE_ILK) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; case 0xfff0: found = 0; for (i = 5; i < 8; i++) { qlm_mode = cvmx_qlm_get_mode_cn78xx(xi.node, i); if (qlm_mode == CVMX_QLM_MODE_ILK) found++; } if (found == 3) iface_node_ops[xi.node][intf] = &iface_ops_ilk; else iface_node_ops[xi.node][intf] = &iface_ops_dis; break; } } else if (xi.interface == 8) { /* DPI */ int qlm = 0; for (qlm = 0; qlm < 5; qlm++) { /* if GSERX_CFG[pcie] == 1, then enable npi */ if (csr_rd_node(xi.node, CVMX_GSERX_CFG(qlm)) & 0x1) { iface_node_ops[xi.node][xi.interface] = &iface_ops_npi; return iface_node_ops[xi.node][xi.interface]->mode; } } iface_node_ops[xi.node][xi.interface] = &iface_ops_dis; } else if (xi.interface == 9) { /* LOOP */ iface_node_ops[xi.node][xi.interface] = &iface_ops_loop; } else { iface_node_ops[xi.node][xi.interface] = &iface_ops_dis; } return iface_node_ops[xi.node][xi.interface]->mode; } /** * @INTERNAL * Return interface mode for CN73XX. */ static cvmx_helper_interface_mode_t __cvmx_get_mode_cn73xx(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); int interface = xi.interface; /* SGMII/XAUI/XLAUI/XFI */ if (interface < 3) { int qlm = cvmx_qlm_lmac(xiface, 0); enum cvmx_qlm_mode qlm_mode; if (qlm == -1) { iface_ops[interface] = &iface_ops_dis; return iface_ops[interface]->mode; } qlm_mode = cvmx_qlm_get_mode(qlm); switch (qlm_mode) { case CVMX_QLM_MODE_SGMII: case CVMX_QLM_MODE_SGMII_2X1: case CVMX_QLM_MODE_RGMII_SGMII: case CVMX_QLM_MODE_RGMII_SGMII_1X1: iface_ops[interface] = &iface_ops_bgx_sgmii; break; case CVMX_QLM_MODE_XAUI: case CVMX_QLM_MODE_RGMII_XAUI: iface_ops[interface] = &iface_ops_bgx_xaui; break; case CVMX_QLM_MODE_RXAUI: case CVMX_QLM_MODE_RXAUI_1X2: case CVMX_QLM_MODE_RGMII_RXAUI: iface_ops[interface] = &iface_ops_bgx_rxaui; break; case CVMX_QLM_MODE_XLAUI: case CVMX_QLM_MODE_RGMII_XLAUI: iface_ops[interface] = &iface_ops_bgx_xlaui; break; case CVMX_QLM_MODE_XFI: case CVMX_QLM_MODE_XFI_1X2: case CVMX_QLM_MODE_RGMII_XFI: iface_ops[interface] = &iface_ops_bgx_xfi; break; case CVMX_QLM_MODE_10G_KR: case CVMX_QLM_MODE_10G_KR_1X2: case CVMX_QLM_MODE_RGMII_10G_KR: iface_ops[interface] = &iface_ops_bgx_10G_KR; break; case CVMX_QLM_MODE_40G_KR4: case CVMX_QLM_MODE_RGMII_40G_KR4: iface_ops[interface] = &iface_ops_bgx_40G_KR4; break; case CVMX_QLM_MODE_MIXED: iface_ops[interface] = &iface_ops_bgx_mixed; break; default: iface_ops[interface] = &iface_ops_dis; break; } } else if (interface == 3) { /* DPI */ iface_ops[interface] = &iface_ops_npi; } else if (interface == 4) { /* LOOP */ iface_ops[interface] = &iface_ops_loop; } else { iface_ops[interface] = &iface_ops_dis; } return iface_ops[interface]->mode; } /** * @INTERNAL * Return interface mode for CNF75XX. * * CNF75XX has a single BGX block, which is attached to two DLMs, * the first, GSER4 only supports SGMII mode, while the second, * GSER5 supports 1G/10G single late modes, i.e. SGMII, XFI, 10G-KR. * Each half-BGX is thus designated as a separate interface with two ports each. */ static cvmx_helper_interface_mode_t __cvmx_get_mode_cnf75xx(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); int interface = xi.interface; /* BGX0: SGMII (DLM4/DLM5)/XFI(DLM5) */ if (interface < 1) { enum cvmx_qlm_mode qlm_mode; int qlm = cvmx_qlm_lmac(xiface, 0); if (qlm == -1) { iface_ops[interface] = &iface_ops_dis; return iface_ops[interface]->mode; } qlm_mode = cvmx_qlm_get_mode(qlm); switch (qlm_mode) { case CVMX_QLM_MODE_SGMII: case CVMX_QLM_MODE_SGMII_2X1: iface_ops[interface] = &iface_ops_bgx_sgmii; break; case CVMX_QLM_MODE_XFI_1X2: iface_ops[interface] = &iface_ops_bgx_xfi; break; case CVMX_QLM_MODE_10G_KR_1X2: iface_ops[interface] = &iface_ops_bgx_10G_KR; break; case CVMX_QLM_MODE_MIXED: iface_ops[interface] = &iface_ops_bgx_mixed; break; default: iface_ops[interface] = &iface_ops_dis; break; } } else if ((interface < 3) && OCTEON_IS_MODEL(OCTEON_CNF75XX)) { /* SRIO is disabled for now */ printf("SRIO disabled for now!\n"); iface_ops[interface] = &iface_ops_dis; } else if (interface == 3) { /* DPI */ iface_ops[interface] = &iface_ops_npi; } else if (interface == 4) { /* LOOP */ iface_ops[interface] = &iface_ops_loop; } else { iface_ops[interface] = &iface_ops_dis; } return iface_ops[interface]->mode; } /** * @INTERNAL * Return interface mode for CN68xx. */ static cvmx_helper_interface_mode_t __cvmx_get_mode_cn68xx(int interface) { union cvmx_mio_qlmx_cfg qlm_cfg; switch (interface) { case 0: qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(0)); /* QLM is disabled when QLM SPD is 15. */ if (qlm_cfg.s.qlm_spd == 15) iface_ops[interface] = &iface_ops_dis; else if (qlm_cfg.s.qlm_cfg == 7) iface_ops[interface] = &iface_ops_rxaui; else if (qlm_cfg.s.qlm_cfg == 2) iface_ops[interface] = &iface_ops_sgmii; else if (qlm_cfg.s.qlm_cfg == 3) iface_ops[interface] = &iface_ops_xaui; else iface_ops[interface] = &iface_ops_dis; break; case 1: qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(0)); /* QLM is disabled when QLM SPD is 15. */ if (qlm_cfg.s.qlm_spd == 15) iface_ops[interface] = &iface_ops_dis; else if (qlm_cfg.s.qlm_cfg == 7) iface_ops[interface] = &iface_ops_rxaui; else iface_ops[interface] = &iface_ops_dis; break; case 2: case 3: case 4: qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(interface)); /* QLM is disabled when QLM SPD is 15. */ if (qlm_cfg.s.qlm_spd == 15) iface_ops[interface] = &iface_ops_dis; else if (qlm_cfg.s.qlm_cfg == 2) iface_ops[interface] = &iface_ops_sgmii; else if (qlm_cfg.s.qlm_cfg == 3) iface_ops[interface] = &iface_ops_xaui; else iface_ops[interface] = &iface_ops_dis; break; case 5: case 6: qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(interface - 4)); /* QLM is disabled when QLM SPD is 15. */ if (qlm_cfg.s.qlm_spd == 15) iface_ops[interface] = &iface_ops_dis; else if (qlm_cfg.s.qlm_cfg == 1) iface_ops[interface] = &iface_ops_ilk; else iface_ops[interface] = &iface_ops_dis; break; case 7: { union cvmx_mio_qlmx_cfg qlm_cfg1; /* Check if PCIe0/PCIe1 is configured for PCIe */ qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(3)); qlm_cfg1.u64 = csr_rd(CVMX_MIO_QLMX_CFG(1)); /* QLM is disabled when QLM SPD is 15. */ if ((qlm_cfg.s.qlm_spd != 15 && qlm_cfg.s.qlm_cfg == 0) || (qlm_cfg1.s.qlm_spd != 15 && qlm_cfg1.s.qlm_cfg == 0)) iface_ops[interface] = &iface_ops_npi; else iface_ops[interface] = &iface_ops_dis; } break; case 8: iface_ops[interface] = &iface_ops_loop; break; default: iface_ops[interface] = &iface_ops_dis; break; } return iface_ops[interface]->mode; } /** * @INTERNAL * Return interface mode for an Octeon II */ static cvmx_helper_interface_mode_t __cvmx_get_mode_octeon2(int interface) { union cvmx_gmxx_inf_mode mode; if (OCTEON_IS_MODEL(OCTEON_CN68XX)) return __cvmx_get_mode_cn68xx(interface); if (interface == 2) { iface_ops[interface] = &iface_ops_npi; } else if (interface == 3) { iface_ops[interface] = &iface_ops_loop; } else if ((OCTEON_IS_MODEL(OCTEON_CN63XX) && (interface == 4 || interface == 5)) || (OCTEON_IS_MODEL(OCTEON_CN66XX) && interface >= 4 && interface <= 7)) { /* Only present in CN63XX & CN66XX Octeon model */ /* cn66xx pass1.0 has only 2 SRIO interfaces. */ if ((interface == 5 || interface == 7) && OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0)) { iface_ops[interface] = &iface_ops_dis; } else if (interface == 5 && OCTEON_IS_MODEL(OCTEON_CN66XX)) { /* * Later passes of cn66xx support SRIO0 - x4/x2/x1, * SRIO2 - x2/x1, SRIO3 - x1 */ iface_ops[interface] = &iface_ops_dis; } else { /* SRIO is disabled for now */ printf("SRIO disabled for now!\n"); iface_ops[interface] = &iface_ops_dis; } } else if (OCTEON_IS_MODEL(OCTEON_CN66XX)) { union cvmx_mio_qlmx_cfg mio_qlm_cfg; /* QLM2 is SGMII0 and QLM1 is SGMII1 */ if (interface == 0) { mio_qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(2)); } else if (interface == 1) { mio_qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(1)); } else { iface_ops[interface] = &iface_ops_dis; return iface_ops[interface]->mode; } if (mio_qlm_cfg.s.qlm_spd == 15) iface_ops[interface] = &iface_ops_dis; else if (mio_qlm_cfg.s.qlm_cfg == 9) iface_ops[interface] = &iface_ops_sgmii; else if (mio_qlm_cfg.s.qlm_cfg == 11) iface_ops[interface] = &iface_ops_xaui; else iface_ops[interface] = &iface_ops_dis; } else if (OCTEON_IS_MODEL(OCTEON_CN61XX)) { union cvmx_mio_qlmx_cfg qlm_cfg; if (interface == 0) { qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(2)); } else if (interface == 1) { qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(0)); } else { iface_ops[interface] = &iface_ops_dis; return iface_ops[interface]->mode; } if (qlm_cfg.s.qlm_spd == 15) iface_ops[interface] = &iface_ops_dis; else if (qlm_cfg.s.qlm_cfg == 2) iface_ops[interface] = &iface_ops_sgmii; else if (qlm_cfg.s.qlm_cfg == 3) iface_ops[interface] = &iface_ops_xaui; else iface_ops[interface] = &iface_ops_dis; } else if (OCTEON_IS_MODEL(OCTEON_CNF71XX)) { if (interface == 0) { union cvmx_mio_qlmx_cfg qlm_cfg; qlm_cfg.u64 = csr_rd(CVMX_MIO_QLMX_CFG(0)); if (qlm_cfg.s.qlm_cfg == 2) iface_ops[interface] = &iface_ops_sgmii; else iface_ops[interface] = &iface_ops_dis; } else { iface_ops[interface] = &iface_ops_dis; } } else if (interface == 1 && OCTEON_IS_MODEL(OCTEON_CN63XX)) { iface_ops[interface] = &iface_ops_dis; } else { mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(interface)); if (OCTEON_IS_MODEL(OCTEON_CN63XX)) { switch (mode.cn63xx.mode) { case 0: iface_ops[interface] = &iface_ops_sgmii; break; case 1: iface_ops[interface] = &iface_ops_xaui; break; default: iface_ops[interface] = &iface_ops_dis; break; } } else { if (!mode.s.en) iface_ops[interface] = &iface_ops_dis; else if (mode.s.type) iface_ops[interface] = &iface_ops_gmii; else iface_ops[interface] = &iface_ops_rgmii; } } return iface_ops[interface]->mode; } /** * Get the operating mode of an interface. Depending on the Octeon * chip and configuration, this function returns an enumeration * of the type of packet I/O supported by an interface. * * @param xiface Interface to probe * * Return: Mode of the interface. Unknown or unsupported interfaces return * DISABLED. */ cvmx_helper_interface_mode_t cvmx_helper_interface_get_mode(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); if (xi.interface < 0 || xi.interface >= cvmx_helper_get_number_of_interfaces()) return CVMX_HELPER_INTERFACE_MODE_DISABLED; /* * Check if the interface mode has been already cached. If it has, * simply return it. Otherwise, fall through the rest of the code to * determine the interface mode and cache it in iface_ops. */ if (iface_node_ops[xi.node][xi.interface]) { cvmx_helper_interface_mode_t mode; mode = iface_node_ops[xi.node][xi.interface]->mode; return mode; } /* * OCTEON III models */ if (OCTEON_IS_MODEL(OCTEON_CN70XX)) return __cvmx_get_mode_cn70xx(xi.interface); if (OCTEON_IS_MODEL(OCTEON_CN78XX)) return __cvmx_get_mode_cn78xx(xiface); if (OCTEON_IS_MODEL(OCTEON_CNF75XX)) { cvmx_helper_interface_mode_t mode; mode = __cvmx_get_mode_cnf75xx(xiface); return mode; } if (OCTEON_IS_MODEL(OCTEON_CN73XX)) { cvmx_helper_interface_mode_t mode; mode = __cvmx_get_mode_cn73xx(xiface); return mode; } /* * Octeon II models */ if (OCTEON_IS_OCTEON2()) return __cvmx_get_mode_octeon2(xi.interface); /* * Octeon and Octeon Plus models */ if (xi.interface == 2) { iface_ops[xi.interface] = &iface_ops_npi; } else if (xi.interface == 3) { iface_ops[xi.interface] = &iface_ops_dis; } else { union cvmx_gmxx_inf_mode mode; mode.u64 = csr_rd(CVMX_GMXX_INF_MODE(xi.interface)); if (!mode.s.en) iface_ops[xi.interface] = &iface_ops_dis; else if (mode.s.type) iface_ops[xi.interface] = &iface_ops_gmii; else iface_ops[xi.interface] = &iface_ops_rgmii; } return iface_ops[xi.interface]->mode; } /** * Determine the actual number of hardware ports connected to an * interface. It doesn't setup the ports or enable them. * * @param xiface Interface to enumerate * * Return: The number of ports on the interface, negative on failure */ int cvmx_helper_interface_enumerate(int xiface) { struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); int result = 0; cvmx_helper_interface_get_mode(xiface); if (iface_node_ops[xi.node][xi.interface]->enumerate) result = iface_node_ops[xi.node][xi.interface]->enumerate(xiface); return result; } /** * This function probes an interface to determine the actual number of * hardware ports connected to it. It does some setup the ports but * doesn't enable them. The main goal here is to set the global * interface_port_count[interface] correctly. Final hardware setup of * the ports will be performed later. * * @param xiface Interface to probe * * Return: Zero on success, negative on failure */ int cvmx_helper_interface_probe(int xiface) { /* * At this stage in the game we don't want packets to be * moving yet. The following probe calls should perform * hardware setup needed to determine port counts. Receive * must still be disabled. */ int nports; int has_fcs; enum cvmx_pko_padding padding = CVMX_PKO_PADDING_NONE; struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); nports = -1; has_fcs = 0; cvmx_helper_interface_get_mode(xiface); if (iface_node_ops[xi.node][xi.interface]->probe) nports = iface_node_ops[xi.node][xi.interface]->probe(xiface); switch (iface_node_ops[xi.node][xi.interface]->mode) { /* These types don't support ports to IPD/PKO */ case CVMX_HELPER_INTERFACE_MODE_DISABLED: case CVMX_HELPER_INTERFACE_MODE_PCIE: nports = 0; break; /* XAUI is a single high speed port */ case CVMX_HELPER_INTERFACE_MODE_XAUI: case CVMX_HELPER_INTERFACE_MODE_RXAUI: case CVMX_HELPER_INTERFACE_MODE_XLAUI: case CVMX_HELPER_INTERFACE_MODE_XFI: case CVMX_HELPER_INTERFACE_MODE_10G_KR: case CVMX_HELPER_INTERFACE_MODE_40G_KR4: case CVMX_HELPER_INTERFACE_MODE_MIXED: has_fcs = 1; padding = CVMX_PKO_PADDING_60; break; /* * RGMII/GMII/MII are all treated about the same. Most * functions refer to these ports as RGMII. */ case CVMX_HELPER_INTERFACE_MODE_RGMII: case CVMX_HELPER_INTERFACE_MODE_GMII: padding = CVMX_PKO_PADDING_60; break; /* * SPI4 can have 1-16 ports depending on the device at * the other end. */ case CVMX_HELPER_INTERFACE_MODE_SPI: padding = CVMX_PKO_PADDING_60; break; /* * SGMII can have 1-4 ports depending on how many are * hooked up. */ case CVMX_HELPER_INTERFACE_MODE_SGMII: case CVMX_HELPER_INTERFACE_MODE_QSGMII: padding = CVMX_PKO_PADDING_60; case CVMX_HELPER_INTERFACE_MODE_PICMG: has_fcs = 1; break; /* PCI target Network Packet Interface */ case CVMX_HELPER_INTERFACE_MODE_NPI: break; /* * Special loopback only ports. These are not the same * as other ports in loopback mode. */ case CVMX_HELPER_INTERFACE_MODE_LOOP: break; /* SRIO has 2^N ports, where N is number of interfaces */ case CVMX_HELPER_INTERFACE_MODE_SRIO: break; case CVMX_HELPER_INTERFACE_MODE_ILK: padding = CVMX_PKO_PADDING_60; has_fcs = 1; break; case CVMX_HELPER_INTERFACE_MODE_AGL: has_fcs = 1; break; } if (nports == -1) return -1; if (!octeon_has_feature(OCTEON_FEATURE_PKND)) has_fcs = 0; nports = __cvmx_helper_board_interface_probe(xiface, nports); __cvmx_helper_init_interface(xiface, nports, has_fcs, padding); /* Make sure all global variables propagate to other cores */ CVMX_SYNCWS; return 0; } /** * @INTERNAL * Setup backpressure. * * Return: Zero on success, negative on failure */ static int __cvmx_helper_global_setup_backpressure(int node) { cvmx_qos_proto_t qos_proto; cvmx_qos_pkt_mode_t qos_mode; int port, xipdport; unsigned int bpmask; int interface, xiface, ports; int num_interfaces = cvmx_helper_get_number_of_interfaces(); if (cvmx_rgmii_backpressure_dis) { qos_proto = CVMX_QOS_PROTO_NONE; qos_mode = CVMX_QOS_PKT_MODE_DROP; } else { qos_proto = CVMX_QOS_PROTO_PAUSE; qos_mode = CVMX_QOS_PKT_MODE_HWONLY; } for (interface = 0; interface < num_interfaces; interface++) { xiface = cvmx_helper_node_interface_to_xiface(node, interface); ports = cvmx_helper_ports_on_interface(xiface); switch (cvmx_helper_interface_get_mode(xiface)) { case CVMX_HELPER_INTERFACE_MODE_DISABLED: case CVMX_HELPER_INTERFACE_MODE_PCIE: case CVMX_HELPER_INTERFACE_MODE_SRIO: case CVMX_HELPER_INTERFACE_MODE_ILK: case CVMX_HELPER_INTERFACE_MODE_NPI: case CVMX_HELPER_INTERFACE_MODE_PICMG: break; case CVMX_HELPER_INTERFACE_MODE_LOOP: case CVMX_HELPER_INTERFACE_MODE_XAUI: case CVMX_HELPER_INTERFACE_MODE_RXAUI: case CVMX_HELPER_INTERFACE_MODE_XLAUI: case CVMX_HELPER_INTERFACE_MODE_XFI: case CVMX_HELPER_INTERFACE_MODE_10G_KR: case CVMX_HELPER_INTERFACE_MODE_40G_KR4: bpmask = (cvmx_rgmii_backpressure_dis) ? 0xF : 0; if (octeon_has_feature(OCTEON_FEATURE_BGX)) { for (port = 0; port < ports; port++) { xipdport = cvmx_helper_get_ipd_port(xiface, port); cvmx_bgx_set_flowctl_mode(xipdport, qos_proto, qos_mode); } cvmx_bgx_set_backpressure_override(xiface, bpmask); } break; case CVMX_HELPER_INTERFACE_MODE_RGMII: case CVMX_HELPER_INTERFACE_MODE_GMII: case CVMX_HELPER_INTERFACE_MODE_SPI: case CVMX_HELPER_INTERFACE_MODE_SGMII: case CVMX_HELPER_INTERFACE_MODE_QSGMII: case CVMX_HELPER_INTERFACE_MODE_MIXED: bpmask = (cvmx_rgmii_backpressure_dis) ? 0xF : 0; if (octeon_has_feature(OCTEON_FEATURE_BGX)) { for (port = 0; port < ports; port++) { xipdport = cvmx_helper_get_ipd_port(xiface, port); cvmx_bgx_set_flowctl_mode(xipdport, qos_proto, qos_mode); } cvmx_bgx_set_backpressure_override(xiface, bpmask); } else { cvmx_gmx_set_backpressure_override(interface, bpmask); } break; case CVMX_HELPER_INTERFACE_MODE_AGL: bpmask = (cvmx_rgmii_backpressure_dis) ? 0x1 : 0; cvmx_agl_set_backpressure_override(interface, bpmask); break; } } return 0; } /** * @INTERNAL * Enable packet input/output from the hardware. This function is * called after all internal setup is complete and IPD is enabled. * After this function completes, packets will be accepted from the * hardware ports. PKO should still be disabled to make sure packets * aren't sent out partially setup hardware. * * @param xiface Interface to enable * * Return: Zero on success, negative on failure */ int __cvmx_helper_packet_hardware_enable(int xiface) { int result = 0; struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); if (iface_node_ops[xi.node][xi.interface]->enable) result = iface_node_ops[xi.node][xi.interface]->enable(xiface); return result; } int cvmx_helper_ipd_and_packet_input_enable(void) { return cvmx_helper_ipd_and_packet_input_enable_node(cvmx_get_node_num()); } /** * Called after all internal packet IO paths are setup. This * function enables IPD/PIP and begins packet input and output. * * Return: Zero on success, negative on failure */ int cvmx_helper_ipd_and_packet_input_enable_node(int node) { int num_interfaces; int interface; int num_ports; if (octeon_has_feature(OCTEON_FEATURE_PKI)) { cvmx_helper_pki_enable(node); } else { /* Enable IPD */ cvmx_ipd_enable(); } /* * Time to enable hardware ports packet input and output. Note * that at this point IPD/PIP must be fully functional and PKO * must be disabled . */ num_interfaces = cvmx_helper_get_number_of_interfaces(); for (interface = 0; interface < num_interfaces; interface++) { int xiface = cvmx_helper_node_interface_to_xiface(node, interface); num_ports = cvmx_helper_ports_on_interface(xiface); if (num_ports > 0) __cvmx_helper_packet_hardware_enable(xiface); } /* Finally enable PKO now that the entire path is up and running */ /* enable pko */ if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) ; // cvmx_pko_enable_78xx(0); already enabled else cvmx_pko_enable(); return 0; } /** * Initialize the PIP, IPD, and PKO hardware to support * simple priority based queues for the ethernet ports. Each * port is configured with a number of priority queues based * on CVMX_PKO_QUEUES_PER_PORT_* where each queue is lower * priority than the previous. * * Return: Zero on success, non-zero on failure */ int cvmx_helper_initialize_packet_io_node(unsigned int node) { int result = 0; int interface; int xiface; union cvmx_l2c_cfg l2c_cfg; union cvmx_smix_en smix_en; const int num_interfaces = cvmx_helper_get_number_of_interfaces(); /* * Tell L2 to give the IOB statically higher priority compared * to the cores. This avoids conditions where IO blocks might * be starved under very high L2 loads. */ if (OCTEON_IS_OCTEON2() || OCTEON_IS_OCTEON3()) { union cvmx_l2c_ctl l2c_ctl; l2c_ctl.u64 = csr_rd_node(node, CVMX_L2C_CTL); l2c_ctl.s.rsp_arb_mode = 1; l2c_ctl.s.xmc_arb_mode = 0; csr_wr_node(node, CVMX_L2C_CTL, l2c_ctl.u64); } else { l2c_cfg.u64 = csr_rd(CVMX_L2C_CFG); l2c_cfg.s.lrf_arb_mode = 0; l2c_cfg.s.rfb_arb_mode = 0; csr_wr(CVMX_L2C_CFG, l2c_cfg.u64); } int smi_inf; int i; /* Newer chips have more than one SMI/MDIO interface */ if (OCTEON_IS_MODEL(OCTEON_CN68XX) || OCTEON_IS_MODEL(OCTEON_CN78XX)) smi_inf = 4; else if (OCTEON_IS_MODEL(OCTEON_CN73XX) || OCTEON_IS_MODEL(OCTEON_CNF75XX)) smi_inf = 2; else smi_inf = 2; for (i = 0; i < smi_inf; i++) { /* Make sure SMI/MDIO is enabled so we can query PHYs */ smix_en.u64 = csr_rd_node(node, CVMX_SMIX_EN(i)); if (!smix_en.s.en) { smix_en.s.en = 1; csr_wr_node(node, CVMX_SMIX_EN(i), smix_en.u64); } } //vinita_to_do ask it need to be modify for multinode __cvmx_helper_init_port_valid(); for (interface = 0; interface < num_interfaces; interface++) { xiface = cvmx_helper_node_interface_to_xiface(node, interface); result |= cvmx_helper_interface_probe(xiface); } /* PKO3 init precedes that of interfaces */ if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { __cvmx_helper_init_port_config_data(node); result = cvmx_helper_pko3_init_global(node); } else { result = cvmx_helper_pko_init(); } /* Errata SSO-29000, Disabling power saving SSO conditional clocking */ if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) { cvmx_sso_ws_cfg_t cfg; cfg.u64 = csr_rd_node(node, CVMX_SSO_WS_CFG); cfg.s.sso_cclk_dis = 1; csr_wr_node(node, CVMX_SSO_WS_CFG, cfg.u64); } if (result < 0) return result; for (interface = 0; interface < num_interfaces; interface++) { xiface = cvmx_helper_node_interface_to_xiface(node, interface); /* Skip invalid/disabled interfaces */ if (cvmx_helper_ports_on_interface(xiface) <= 0) continue; debug("Node %d Interface %d has %d ports (%s)\n", node, interface, cvmx_helper_ports_on_interface(xiface), cvmx_helper_interface_mode_to_string( cvmx_helper_interface_get_mode(xiface))); result |= __cvmx_helper_ipd_setup_interface(xiface); if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) result |= cvmx_helper_pko3_init_interface(xiface); else result |= __cvmx_helper_interface_setup_pko(interface); } if (octeon_has_feature(OCTEON_FEATURE_PKI)) result |= __cvmx_helper_pki_global_setup(node); else result |= __cvmx_helper_ipd_global_setup(); /* Enable any flow control and backpressure */ result |= __cvmx_helper_global_setup_backpressure(node); /* export app config if set */ if (cvmx_export_app_config) result |= (*cvmx_export_app_config)(); if (cvmx_ipd_cfg.ipd_enable && cvmx_pki_dflt_init[node]) result |= cvmx_helper_ipd_and_packet_input_enable_node(node); return result; } /** * Initialize the PIP, IPD, and PKO hardware to support * simple priority based queues for the ethernet ports. Each * port is configured with a number of priority queues based * on CVMX_PKO_QUEUES_PER_PORT_* where each queue is lower * priority than the previous. * * Return: Zero on success, non-zero on failure */ int cvmx_helper_initialize_packet_io_global(void) { unsigned int node = cvmx_get_node_num(); return cvmx_helper_initialize_packet_io_node(node); } /** * Does core local initialization for packet io * * Return: Zero on success, non-zero on failure */ int cvmx_helper_initialize_packet_io_local(void) { if (octeon_has_feature(OCTEON_FEATURE_CN78XX_WQE)) __cvmx_pko3_dq_table_setup(); return 0; } struct cvmx_buffer_list { struct cvmx_buffer_list *next; }; /** * Disables the sending of flow control (pause) frames on the specified * GMX port(s). * * @param interface Which interface (0 or 1) * @param port_mask Mask (4bits) of which ports on the interface to disable * backpressure on. * 1 => disable backpressure * 0 => enable backpressure * * Return: 0 on success * -1 on error */ int cvmx_gmx_set_backpressure_override(u32 interface, uint32_t port_mask) { union cvmx_gmxx_tx_ovr_bp gmxx_tx_ovr_bp; /* Check for valid arguments */ if (port_mask & ~0xf || interface & ~0x1) return -1; if (interface >= CVMX_HELPER_MAX_GMX) return -1; gmxx_tx_ovr_bp.u64 = 0; gmxx_tx_ovr_bp.s.en = port_mask; /* Per port Enable back pressure override */ gmxx_tx_ovr_bp.s.ign_full = port_mask; /* Ignore the RX FIFO full when computing BP */ csr_wr(CVMX_GMXX_TX_OVR_BP(interface), gmxx_tx_ovr_bp.u64); return 0; } /** * Disables the sending of flow control (pause) frames on the specified * AGL (RGMII) port(s). * * @param interface Which interface (0 or 1) * @param port_mask Mask (4bits) of which ports on the interface to disable * backpressure on. * 1 => disable backpressure * 0 => enable backpressure * * Return: 0 on success * -1 on error */ int cvmx_agl_set_backpressure_override(u32 interface, uint32_t port_mask) { union cvmx_agl_gmx_tx_ovr_bp agl_gmx_tx_ovr_bp; int port = cvmx_helper_agl_get_port(interface); if (port == -1) return -1; /* Check for valid arguments */ agl_gmx_tx_ovr_bp.u64 = 0; /* Per port Enable back pressure override */ agl_gmx_tx_ovr_bp.s.en = port_mask; /* Ignore the RX FIFO full when computing BP */ agl_gmx_tx_ovr_bp.s.ign_full = port_mask; csr_wr(CVMX_GMXX_TX_OVR_BP(port), agl_gmx_tx_ovr_bp.u64); return 0; } /** * Auto configure an IPD/PKO port link state and speed. This * function basically does the equivalent of: * cvmx_helper_link_set(ipd_port, cvmx_helper_link_get(ipd_port)); * * @param xipd_port IPD/PKO port to auto configure * * Return: Link state after configure */ cvmx_helper_link_info_t cvmx_helper_link_autoconf(int xipd_port) { cvmx_helper_link_info_t link_info; int xiface = cvmx_helper_get_interface_num(xipd_port); int index = cvmx_helper_get_interface_index_num(xipd_port); struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); int interface = xi.interface; if (interface == -1 || index == -1 || index >= cvmx_helper_ports_on_interface(xiface)) { link_info.u64 = 0; return link_info; } link_info = cvmx_helper_link_get(xipd_port); if (link_info.u64 == (__cvmx_helper_get_link_info(xiface, index)).u64) return link_info; if (!link_info.s.link_up) cvmx_error_disable_group(CVMX_ERROR_GROUP_ETHERNET, xipd_port); /* If we fail to set the link speed, port_link_info will not change */ cvmx_helper_link_set(xipd_port, link_info); if (link_info.s.link_up) cvmx_error_enable_group(CVMX_ERROR_GROUP_ETHERNET, xipd_port); return link_info; } /** * Return the link state of an IPD/PKO port as returned by * auto negotiation. The result of this function may not match * Octeon's link config if auto negotiation has changed since * the last call to cvmx_helper_link_set(). * * @param xipd_port IPD/PKO port to query * * Return: Link state */ cvmx_helper_link_info_t cvmx_helper_link_get(int xipd_port) { cvmx_helper_link_info_t result; int xiface = cvmx_helper_get_interface_num(xipd_port); int index = cvmx_helper_get_interface_index_num(xipd_port); struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); struct cvmx_fdt_sfp_info *sfp_info; /* * The default result will be a down link unless the code * below changes it. */ result.u64 = 0; if (__cvmx_helper_xiface_is_null(xiface) || index == -1 || index >= cvmx_helper_ports_on_interface(xiface)) { return result; } if (iface_node_ops[xi.node][xi.interface]->link_get) result = iface_node_ops[xi.node][xi.interface]->link_get(xipd_port); if (xipd_port >= 0) { cvmx_helper_update_link_led(xiface, index, result); sfp_info = cvmx_helper_cfg_get_sfp_info(xiface, index); while (sfp_info) { if ((!result.s.link_up || (result.s.link_up && sfp_info->last_mod_abs))) cvmx_sfp_check_mod_abs(sfp_info, sfp_info->mod_abs_data); sfp_info = sfp_info->next_iface_sfp; } } return result; } /** * Configure an IPD/PKO port for the specified link state. This * function does not influence auto negotiation at the PHY level. * The passed link state must always match the link state returned * by cvmx_helper_link_get(). It is normally best to use * cvmx_helper_link_autoconf() instead. * * @param xipd_port IPD/PKO port to configure * @param link_info The new link state * * Return: Zero on success, negative on failure */ int cvmx_helper_link_set(int xipd_port, cvmx_helper_link_info_t link_info) { int result = -1; int xiface = cvmx_helper_get_interface_num(xipd_port); struct cvmx_xiface xi = cvmx_helper_xiface_to_node_interface(xiface); int index = cvmx_helper_get_interface_index_num(xipd_port); if (__cvmx_helper_xiface_is_null(xiface) || index == -1 || index >= cvmx_helper_ports_on_interface(xiface)) return -1; if (iface_node_ops[xi.node][xi.interface]->link_set) result = iface_node_ops[xi.node][xi.interface]->link_set(xipd_port, link_info); /* * Set the port_link_info here so that the link status is * updated no matter how cvmx_helper_link_set is called. We * don't change the value if link_set failed. */ if (result == 0) __cvmx_helper_set_link_info(xiface, index, link_info); return result; } void *cvmx_helper_mem_alloc(int node, uint64_t alloc_size, uint64_t align) { s64 paddr; paddr = cvmx_bootmem_phy_alloc_range(alloc_size, align, cvmx_addr_on_node(node, 0ull), cvmx_addr_on_node(node, 0xffffffffff)); if (paddr <= 0ll) { printf("ERROR: %s failed size %u\n", __func__, (unsigned int)alloc_size); return NULL; } return cvmx_phys_to_ptr(paddr); }