// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2015-2017 Netronome Systems, Inc. */ /* Authors: David Brunecz * Jakub Kicinski * Jason Mcmullan */ #include #include #include #include #include #include "nfp.h" #include "nfp_nsp.h" #include "nfp6000/nfp6000.h" #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 cpu_to_le64(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) 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 { __le64 port; __le64 state; u8 mac_addr[6]; u8 resv[2]; __le64 control; }; __le64 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 < 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 < 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(u8 *dst, const u8 *src) { int i; for (i = 0; i < 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; u64 port, state; port = le64_to_cpu(src->port); state = le64_to_cpu(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_cpp *cpp, struct nfp_eth_table *table) { unsigned int i, j; for (i = 0; i < table->count; i++) { table->max_index = 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) nfp_warn(cpp, "Port %d subport %d is a duplicate\n", table->ports[i].label_port, table->ports[i].label_subport); table->ports[i].is_split = true; } } } static void nfp_eth_calc_port_type(struct nfp_cpp *cpp, struct nfp_eth_table_port *entry) { if (entry->interface == NFP_INTERFACE_NONE) { entry->port_type = PORT_NONE; return; } else if (entry->interface == NFP_INTERFACE_RJ45) { entry->port_type = PORT_TP; return; } if (entry->media == NFP_MEDIA_FIBRE) entry->port_type = PORT_FIBRE; else entry->port_type = PORT_DA; } /** * 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 (IS_ERR(nsp)) return NULL; ret = __nfp_eth_read_ports(cpp, nsp); nfp_nsp_close(nsp); return ret; } struct nfp_eth_table * __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) { union eth_table_entry *entries; struct nfp_eth_table *table; int i, j, ret, cnt = 0; entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); if (!entries) return NULL; ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); if (ret < 0) { nfp_err(cpp, "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) { nfp_err(cpp, "table entry count reported (%d) does not match entries present (%d)\n", ret, cnt); goto err; } table = kzalloc(struct_size(table, ports, cnt), GFP_KERNEL); if (!table) goto err; 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(cpp, table); for (i = 0; i < table->count; i++) nfp_eth_calc_port_type(cpp, &table->ports[i]); kfree(entries); return table; err: kfree(entries); return NULL; } 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 = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); if (!entries) return ERR_PTR(-ENOMEM); nsp = nfp_nsp_open(cpp); if (IS_ERR(nsp)) { kfree(entries); return nsp; } ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); if (ret < 0) { nfp_err(cpp, "reading port table failed %d\n", ret); goto err; } if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) { nfp_warn(cpp, "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); kfree(entries); return ERR_PTR(-EIO); } 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, false); nfp_nsp_config_clear_state(nsp); nfp_nsp_close(nsp); kfree(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, bool enable) { union eth_table_entry *entries; struct nfp_nsp *nsp; u64 reg; nsp = nfp_eth_config_start(cpp, idx); if (IS_ERR(nsp)) return PTR_ERR(nsp); entries = nfp_nsp_config_entries(nsp); /* Check if we are already in requested state */ reg = le64_to_cpu(entries[idx].state); if (enable != FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) { reg = le64_to_cpu(entries[idx].control); reg &= ~NSP_ETH_CTRL_ENABLED; reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); entries[idx].control = cpu_to_le64(reg); nfp_nsp_config_set_modified(nsp, true); } 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, bool configed) { union eth_table_entry *entries; struct nfp_nsp *nsp; u64 reg; nsp = nfp_eth_config_start(cpp, idx); if (IS_ERR(nsp)) return PTR_ERR(nsp); /* 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 = le64_to_cpu(entries[idx].state); if (configed != FIELD_GET(NSP_ETH_STATE_CONFIGURED, reg)) { reg = le64_to_cpu(entries[idx].control); reg &= ~NSP_ETH_CTRL_CONFIGURED; reg |= FIELD_PREP(NSP_ETH_CTRL_CONFIGURED, configed); entries[idx].control = cpu_to_le64(reg); nfp_nsp_config_set_modified(nsp, true); } return nfp_eth_config_commit_end(nsp); } static int nfp_eth_set_bit_config(struct nfp_nsp *nsp, unsigned int raw_idx, const u64 mask, const unsigned int shift, unsigned int val, const u64 ctrl_bit) { union eth_table_entry *entries = nfp_nsp_config_entries(nsp); unsigned int idx = nfp_nsp_config_idx(nsp); u64 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) { nfp_err(nfp_nsp_cpp(nsp), "set operations not supported, please update flash\n"); return -EOPNOTSUPP; } /* Check if we are already in requested state */ reg = le64_to_cpu(entries[idx].raw[raw_idx]); if (val == (reg & mask) >> shift) return 0; reg &= ~mask; reg |= (val << shift) & mask; entries[idx].raw[raw_idx] = cpu_to_le64(reg); entries[idx].control |= cpu_to_le64(ctrl_bit); nfp_nsp_config_set_modified(nsp, true); return 0; } #define NFP_ETH_SET_BIT_CONFIG(nsp, raw_idx, mask, val, ctrl_bit) \ ({ \ __BF_FIELD_CHECK(mask, 0ULL, val, "NFP_ETH_SET_BIT_CONFIG: "); \ nfp_eth_set_bit_config(nsp, raw_idx, mask, __bf_shf(mask), \ 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 (IS_ERR(nsp)) return PTR_ERR(nsp); 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) { nfp_warn(nfp_nsp_cpp(nsp), "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); }