/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2018 Netronome Systems, Inc. * All rights reserved. */ #include #include #include #include "nfp_cpp.h" #include "nfp_nsp.h" #include "nfp6000/nfp6000.h" #define GENMASK_ULL(h, l) \ (((~0ULL) - (1ULL << (l)) + 1) & \ (~0ULL >> (64 - 1 - (h)))) #define __bf_shf(x) (__builtin_ffsll(x) - 1) #define FIELD_GET(_mask, _reg) \ (__extension__ ({ \ typeof(_mask) _x = (_mask); \ (typeof(_x))(((_reg) & (_x)) >> __bf_shf(_x)); \ })) #define FIELD_FIT(_mask, _val) \ (__extension__ ({ \ typeof(_mask) _x = (_mask); \ !((((typeof(_x))_val) << __bf_shf(_x)) & ~(_x)); \ })) #define FIELD_PREP(_mask, _val) \ (__extension__ ({ \ typeof(_mask) _x = (_mask); \ ((typeof(_x))(_val) << __bf_shf(_x)) & (_x); \ })) #define NSP_ETH_NBI_PORT_COUNT 24 #define NSP_ETH_MAX_COUNT (2 * NSP_ETH_NBI_PORT_COUNT) #define NSP_ETH_TABLE_SIZE (NSP_ETH_MAX_COUNT * \ sizeof(union eth_table_entry)) #define NSP_ETH_PORT_LANES GENMASK_ULL(3, 0) #define NSP_ETH_PORT_INDEX GENMASK_ULL(15, 8) #define NSP_ETH_PORT_LABEL GENMASK_ULL(53, 48) #define NSP_ETH_PORT_PHYLABEL GENMASK_ULL(59, 54) #define NSP_ETH_PORT_FEC_SUPP_BASER BIT_ULL(60) #define NSP_ETH_PORT_FEC_SUPP_RS BIT_ULL(61) #define NSP_ETH_PORT_LANES_MASK rte_cpu_to_le_64(NSP_ETH_PORT_LANES) #define NSP_ETH_STATE_CONFIGURED BIT_ULL(0) #define NSP_ETH_STATE_ENABLED BIT_ULL(1) #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) #define NSP_ETH_STATE_INTERFACE GENMASK_ULL(19, 12) #define NSP_ETH_STATE_MEDIA GENMASK_ULL(21, 20) #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) #define NSP_ETH_STATE_FEC GENMASK_ULL(27, 26) #define NSP_ETH_CTRL_CONFIGURED BIT_ULL(0) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) #define NSP_ETH_CTRL_RX_ENABLED BIT_ULL(3) #define NSP_ETH_CTRL_SET_RATE BIT_ULL(4) #define NSP_ETH_CTRL_SET_LANES BIT_ULL(5) #define NSP_ETH_CTRL_SET_ANEG BIT_ULL(6) #define NSP_ETH_CTRL_SET_FEC BIT_ULL(7) /* Which connector port. */ #define PORT_TP 0x00 #define PORT_AUI 0x01 #define PORT_MII 0x02 #define PORT_FIBRE 0x03 #define PORT_BNC 0x04 #define PORT_DA 0x05 #define PORT_NONE 0xef #define PORT_OTHER 0xff #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 #define SPEED_2500 2500 #define SPEED_5000 5000 #define SPEED_10000 10000 #define SPEED_14000 14000 #define SPEED_20000 20000 #define SPEED_25000 25000 #define SPEED_40000 40000 #define SPEED_50000 50000 #define SPEED_56000 56000 #define SPEED_100000 100000 enum nfp_eth_raw { NSP_ETH_RAW_PORT = 0, NSP_ETH_RAW_STATE, NSP_ETH_RAW_MAC, NSP_ETH_RAW_CONTROL, NSP_ETH_NUM_RAW }; enum nfp_eth_rate { RATE_INVALID = 0, RATE_10M, RATE_100M, RATE_1G, RATE_10G, RATE_25G, }; union eth_table_entry { struct { uint64_t port; uint64_t state; uint8_t mac_addr[6]; uint8_t resv[2]; uint64_t control; }; uint64_t raw[NSP_ETH_NUM_RAW]; }; static const struct { enum nfp_eth_rate rate; unsigned int speed; } nsp_eth_rate_tbl[] = { { RATE_INVALID, 0, }, { RATE_10M, SPEED_10, }, { RATE_100M, SPEED_100, }, { RATE_1G, SPEED_1000, }, { RATE_10G, SPEED_10000, }, { RATE_25G, SPEED_25000, }, }; static unsigned int nfp_eth_rate2speed(enum nfp_eth_rate rate) { int i; for (i = 0; i < (int)ARRAY_SIZE(nsp_eth_rate_tbl); i++) if (nsp_eth_rate_tbl[i].rate == rate) return nsp_eth_rate_tbl[i].speed; return 0; } static unsigned int nfp_eth_speed2rate(unsigned int speed) { int i; for (i = 0; i < (int)ARRAY_SIZE(nsp_eth_rate_tbl); i++) if (nsp_eth_rate_tbl[i].speed == speed) return nsp_eth_rate_tbl[i].rate; return RATE_INVALID; } static void nfp_eth_copy_mac_reverse(uint8_t *dst, const uint8_t *src) { int i; for (i = 0; i < (int)ETH_ALEN; i++) dst[ETH_ALEN - i - 1] = src[i]; } static void nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src, unsigned int index, struct nfp_eth_table_port *dst) { unsigned int rate; unsigned int fec; uint64_t port, state; port = rte_le_to_cpu_64(src->port); state = rte_le_to_cpu_64(src->state); dst->eth_index = FIELD_GET(NSP_ETH_PORT_INDEX, port); dst->index = index; dst->nbi = index / NSP_ETH_NBI_PORT_COUNT; dst->base = index % NSP_ETH_NBI_PORT_COUNT; dst->lanes = FIELD_GET(NSP_ETH_PORT_LANES, port); dst->enabled = FIELD_GET(NSP_ETH_STATE_ENABLED, state); dst->tx_enabled = FIELD_GET(NSP_ETH_STATE_TX_ENABLED, state); dst->rx_enabled = FIELD_GET(NSP_ETH_STATE_RX_ENABLED, state); rate = nfp_eth_rate2speed(FIELD_GET(NSP_ETH_STATE_RATE, state)); dst->speed = dst->lanes * rate; dst->interface = FIELD_GET(NSP_ETH_STATE_INTERFACE, state); dst->media = FIELD_GET(NSP_ETH_STATE_MEDIA, state); nfp_eth_copy_mac_reverse(dst->mac_addr, src->mac_addr); dst->label_port = FIELD_GET(NSP_ETH_PORT_PHYLABEL, port); dst->label_subport = FIELD_GET(NSP_ETH_PORT_LABEL, port); if (nfp_nsp_get_abi_ver_minor(nsp) < 17) return; dst->override_changed = FIELD_GET(NSP_ETH_STATE_OVRD_CHNG, state); dst->aneg = FIELD_GET(NSP_ETH_STATE_ANEG, state); if (nfp_nsp_get_abi_ver_minor(nsp) < 22) return; fec = FIELD_GET(NSP_ETH_PORT_FEC_SUPP_BASER, port); dst->fec_modes_supported |= fec << NFP_FEC_BASER_BIT; fec = FIELD_GET(NSP_ETH_PORT_FEC_SUPP_RS, port); dst->fec_modes_supported |= fec << NFP_FEC_REED_SOLOMON_BIT; if (dst->fec_modes_supported) dst->fec_modes_supported |= NFP_FEC_AUTO | NFP_FEC_DISABLED; dst->fec = 1 << FIELD_GET(NSP_ETH_STATE_FEC, state); } static void nfp_eth_calc_port_geometry(struct nfp_eth_table *table) { unsigned int i, j; for (i = 0; i < table->count; i++) { table->max_index = RTE_MAX(table->max_index, table->ports[i].index); for (j = 0; j < table->count; j++) { if (table->ports[i].label_port != table->ports[j].label_port) continue; table->ports[i].port_lanes += table->ports[j].lanes; if (i == j) continue; if (table->ports[i].label_subport == table->ports[j].label_subport) printf("Port %d subport %d is a duplicate\n", table->ports[i].label_port, table->ports[i].label_subport); table->ports[i].is_split = 1; } } } static void nfp_eth_calc_port_type(struct nfp_eth_table_port *entry) { if (entry->interface == NFP_INTERFACE_NONE) { entry->port_type = PORT_NONE; return; } if (entry->media == NFP_MEDIA_FIBRE) entry->port_type = PORT_FIBRE; else entry->port_type = PORT_DA; } static struct nfp_eth_table * __nfp_eth_read_ports(struct nfp_nsp *nsp) { union eth_table_entry *entries; struct nfp_eth_table *table; uint32_t table_sz; int i, j, ret, cnt = 0; entries = malloc(NSP_ETH_TABLE_SIZE); if (!entries) return NULL; memset(entries, 0, NSP_ETH_TABLE_SIZE); ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); if (ret < 0) { printf("reading port table failed %d\n", ret); goto err; } for (i = 0; i < NSP_ETH_MAX_COUNT; i++) if (entries[i].port & NSP_ETH_PORT_LANES_MASK) cnt++; /* Some versions of flash will give us 0 instead of port count. For * those that give a port count, verify it against the value calculated * above. */ if (ret && ret != cnt) { printf("table entry count (%d) unmatch entries present (%d)\n", ret, cnt); goto err; } table_sz = sizeof(*table) + sizeof(struct nfp_eth_table_port) * cnt; table = malloc(table_sz); if (!table) goto err; memset(table, 0, table_sz); table->count = cnt; for (i = 0, j = 0; i < NSP_ETH_MAX_COUNT; i++) if (entries[i].port & NSP_ETH_PORT_LANES_MASK) nfp_eth_port_translate(nsp, &entries[i], i, &table->ports[j++]); nfp_eth_calc_port_geometry(table); for (i = 0; i < (int)table->count; i++) nfp_eth_calc_port_type(&table->ports[i]); free(entries); return table; err: free(entries); return NULL; } /* * nfp_eth_read_ports() - retrieve port information * @cpp: NFP CPP handle * * Read the port information from the device. Returned structure should * be freed with kfree() once no longer needed. * * Return: populated ETH table or NULL on error. */ struct nfp_eth_table * nfp_eth_read_ports(struct nfp_cpp *cpp) { struct nfp_eth_table *ret; struct nfp_nsp *nsp; nsp = nfp_nsp_open(cpp); if (!nsp) return NULL; ret = __nfp_eth_read_ports(nsp); nfp_nsp_close(nsp); return ret; } struct nfp_nsp * nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx) { union eth_table_entry *entries; struct nfp_nsp *nsp; int ret; entries = malloc(NSP_ETH_TABLE_SIZE); if (!entries) return NULL; memset(entries, 0, NSP_ETH_TABLE_SIZE); nsp = nfp_nsp_open(cpp); if (!nsp) { free(entries); return nsp; } ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); if (ret < 0) { printf("reading port table failed %d\n", ret); goto err; } if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) { printf("trying to set port state on disabled port %d\n", idx); goto err; } nfp_nsp_config_set_state(nsp, entries, idx); return nsp; err: nfp_nsp_close(nsp); free(entries); return NULL; } void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) { union eth_table_entry *entries = nfp_nsp_config_entries(nsp); nfp_nsp_config_set_modified(nsp, 0); nfp_nsp_config_clear_state(nsp); nfp_nsp_close(nsp); free(entries); } /* * nfp_eth_config_commit_end() - perform recorded configuration changes * @nsp: NFP NSP handle returned from nfp_eth_config_start() * * Perform the configuration which was requested with __nfp_eth_set_*() * helpers and recorded in @nsp state. If device was already configured * as requested or no __nfp_eth_set_*() operations were made no NSP command * will be performed. * * Return: * 0 - configuration successful; * 1 - no changes were needed; * -ERRNO - configuration failed. */ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) { union eth_table_entry *entries = nfp_nsp_config_entries(nsp); int ret = 1; if (nfp_nsp_config_modified(nsp)) { ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); ret = ret < 0 ? ret : 0; } nfp_eth_config_cleanup_end(nsp); return ret; } /* * nfp_eth_set_mod_enable() - set PHY module enable control bit * @cpp: NFP CPP handle * @idx: NFP chip-wide port index * @enable: Desired state * * Enable or disable PHY module (this usually means setting the TX lanes * disable bits). * * Return: * 0 - configuration successful; * 1 - no changes were needed; * -ERRNO - configuration failed. */ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, int enable) { union eth_table_entry *entries; struct nfp_nsp *nsp; uint64_t reg; nsp = nfp_eth_config_start(cpp, idx); if (!nsp) return -1; entries = nfp_nsp_config_entries(nsp); /* Check if we are already in requested state */ reg = rte_le_to_cpu_64(entries[idx].state); if (enable != (int)FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) { reg = rte_le_to_cpu_64(entries[idx].control); reg &= ~NSP_ETH_CTRL_ENABLED; reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); entries[idx].control = rte_cpu_to_le_64(reg); nfp_nsp_config_set_modified(nsp, 1); } return nfp_eth_config_commit_end(nsp); } /* * nfp_eth_set_configured() - set PHY module configured control bit * @cpp: NFP CPP handle * @idx: NFP chip-wide port index * @configed: Desired state * * Set the ifup/ifdown state on the PHY. * * Return: * 0 - configuration successful; * 1 - no changes were needed; * -ERRNO - configuration failed. */ int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, int configed) { union eth_table_entry *entries; struct nfp_nsp *nsp; uint64_t reg; nsp = nfp_eth_config_start(cpp, idx); if (!nsp) return -EIO; /* * Older ABI versions did support this feature, however this has only * been reliable since ABI 20. */ if (nfp_nsp_get_abi_ver_minor(nsp) < 20) { nfp_eth_config_cleanup_end(nsp); return -EOPNOTSUPP; } entries = nfp_nsp_config_entries(nsp); /* Check if we are already in requested state */ reg = rte_le_to_cpu_64(entries[idx].state); if (configed != (int)FIELD_GET(NSP_ETH_STATE_CONFIGURED, reg)) { reg = rte_le_to_cpu_64(entries[idx].control); reg &= ~NSP_ETH_CTRL_CONFIGURED; reg |= FIELD_PREP(NSP_ETH_CTRL_CONFIGURED, configed); entries[idx].control = rte_cpu_to_le_64(reg); nfp_nsp_config_set_modified(nsp, 1); } return nfp_eth_config_commit_end(nsp); } static int nfp_eth_set_bit_config(struct nfp_nsp *nsp, unsigned int raw_idx, const uint64_t mask, const unsigned int shift, unsigned int val, const uint64_t ctrl_bit) { union eth_table_entry *entries = nfp_nsp_config_entries(nsp); unsigned int idx = nfp_nsp_config_idx(nsp); uint64_t reg; /* * Note: set features were added in ABI 0.14 but the error * codes were initially not populated correctly. */ if (nfp_nsp_get_abi_ver_minor(nsp) < 17) { printf("set operations not supported, please update flash\n"); return -EOPNOTSUPP; } /* Check if we are already in requested state */ reg = rte_le_to_cpu_64(entries[idx].raw[raw_idx]); if (val == (reg & mask) >> shift) return 0; reg &= ~mask; reg |= (val << shift) & mask; entries[idx].raw[raw_idx] = rte_cpu_to_le_64(reg); entries[idx].control |= rte_cpu_to_le_64(ctrl_bit); nfp_nsp_config_set_modified(nsp, 1); return 0; } #define NFP_ETH_SET_BIT_CONFIG(nsp, raw_idx, mask, val, ctrl_bit) \ (__extension__ ({ \ typeof(mask) _x = (mask); \ nfp_eth_set_bit_config(nsp, raw_idx, _x, __bf_shf(_x), \ val, ctrl_bit); \ })) /* * __nfp_eth_set_aneg() - set PHY autonegotiation control bit * @nsp: NFP NSP handle returned from nfp_eth_config_start() * @mode: Desired autonegotiation mode * * Allow/disallow PHY module to advertise/perform autonegotiation. * Will write to hwinfo overrides in the flash (persistent config). * * Return: 0 or -ERRNO. */ int __nfp_eth_set_aneg(struct nfp_nsp *nsp, enum nfp_eth_aneg mode) { return NFP_ETH_SET_BIT_CONFIG(nsp, NSP_ETH_RAW_STATE, NSP_ETH_STATE_ANEG, mode, NSP_ETH_CTRL_SET_ANEG); } /* * __nfp_eth_set_fec() - set PHY forward error correction control bit * @nsp: NFP NSP handle returned from nfp_eth_config_start() * @mode: Desired fec mode * * Set the PHY module forward error correction mode. * Will write to hwinfo overrides in the flash (persistent config). * * Return: 0 or -ERRNO. */ static int __nfp_eth_set_fec(struct nfp_nsp *nsp, enum nfp_eth_fec mode) { return NFP_ETH_SET_BIT_CONFIG(nsp, NSP_ETH_RAW_STATE, NSP_ETH_STATE_FEC, mode, NSP_ETH_CTRL_SET_FEC); } /* * nfp_eth_set_fec() - set PHY forward error correction control mode * @cpp: NFP CPP handle * @idx: NFP chip-wide port index * @mode: Desired fec mode * * Return: * 0 - configuration successful; * 1 - no changes were needed; * -ERRNO - configuration failed. */ int nfp_eth_set_fec(struct nfp_cpp *cpp, unsigned int idx, enum nfp_eth_fec mode) { struct nfp_nsp *nsp; int err; nsp = nfp_eth_config_start(cpp, idx); if (!nsp) return -EIO; err = __nfp_eth_set_fec(nsp, mode); if (err) { nfp_eth_config_cleanup_end(nsp); return err; } return nfp_eth_config_commit_end(nsp); } /* * __nfp_eth_set_speed() - set interface speed/rate * @nsp: NFP NSP handle returned from nfp_eth_config_start() * @speed: Desired speed (per lane) * * Set lane speed. Provided @speed value should be subport speed divided * by number of lanes this subport is spanning (i.e. 10000 for 40G, 25000 for * 50G, etc.) * Will write to hwinfo overrides in the flash (persistent config). * * Return: 0 or -ERRNO. */ int __nfp_eth_set_speed(struct nfp_nsp *nsp, unsigned int speed) { enum nfp_eth_rate rate; rate = nfp_eth_speed2rate(speed); if (rate == RATE_INVALID) { printf("could not find matching lane rate for speed %u\n", speed); return -EINVAL; } return NFP_ETH_SET_BIT_CONFIG(nsp, NSP_ETH_RAW_STATE, NSP_ETH_STATE_RATE, rate, NSP_ETH_CTRL_SET_RATE); } /* * __nfp_eth_set_split() - set interface lane split * @nsp: NFP NSP handle returned from nfp_eth_config_start() * @lanes: Desired lanes per port * * Set number of lanes in the port. * Will write to hwinfo overrides in the flash (persistent config). * * Return: 0 or -ERRNO. */ int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes) { return NFP_ETH_SET_BIT_CONFIG(nsp, NSP_ETH_RAW_PORT, NSP_ETH_PORT_LANES, lanes, NSP_ETH_CTRL_SET_LANES); }