diff options
Diffstat (limited to 'arch/mips/mach-octeon/cvmx-helper.c')
-rw-r--r-- | arch/mips/mach-octeon/cvmx-helper.c | 1788 |
1 files changed, 1788 insertions, 0 deletions
diff --git a/arch/mips/mach-octeon/cvmx-helper.c b/arch/mips/mach-octeon/cvmx-helper.c new file mode 100644 index 00000000000..7dcaa1ac8d9 --- /dev/null +++ b/arch/mips/mach-octeon/cvmx-helper.c @@ -0,0 +1,1788 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Marvell International Ltd. + * + * Helper functions for common, but complicated tasks. + */ + +#include <log.h> +#include <linux/delay.h> + +#include <mach/cvmx-regs.h> +#include <mach/cvmx-csr.h> +#include <mach/cvmx-bootmem.h> +#include <mach/octeon-model.h> +#include <mach/cvmx-fuse.h> +#include <mach/octeon-feature.h> +#include <mach/cvmx-qlm.h> +#include <mach/octeon_qlm.h> +#include <mach/cvmx-pcie.h> +#include <mach/cvmx-coremask.h> + +#include <mach/cvmx-agl-defs.h> +#include <mach/cvmx-asxx-defs.h> +#include <mach/cvmx-bgxx-defs.h> +#include <mach/cvmx-dbg-defs.h> +#include <mach/cvmx-gmxx-defs.h> +#include <mach/cvmx-gserx-defs.h> +#include <mach/cvmx-ipd-defs.h> +#include <mach/cvmx-l2c-defs.h> +#include <mach/cvmx-npi-defs.h> +#include <mach/cvmx-pcsx-defs.h> +#include <mach/cvmx-pexp-defs.h> +#include <mach/cvmx-pki-defs.h> +#include <mach/cvmx-pko-defs.h> +#include <mach/cvmx-smix-defs.h> +#include <mach/cvmx-sriox-defs.h> +#include <mach/cvmx-helper.h> +#include <mach/cvmx-helper-board.h> +#include <mach/cvmx-helper-fdt.h> +#include <mach/cvmx-helper-bgx.h> +#include <mach/cvmx-helper-cfg.h> +#include <mach/cvmx-helper-ipd.h> +#include <mach/cvmx-helper-util.h> +#include <mach/cvmx-helper-pki.h> +#include <mach/cvmx-helper-pko.h> +#include <mach/cvmx-helper-pko3.h> +#include <mach/cvmx-global-resources.h> +#include <mach/cvmx-pko-internal-ports-range.h> +#include <mach/cvmx-pko3-queue.h> +#include <mach/cvmx-gmx.h> +#include <mach/cvmx-hwpko.h> +#include <mach/cvmx-ilk.h> +#include <mach/cvmx-ipd.h> +#include <mach/cvmx-pip.h> + +/** + * @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 || 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); +} |