forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
9.3 KiB
405 lines
9.3 KiB
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
|
/* Copyright (C) 2017-2018 Netronome Systems, Inc. */ |
|
|
|
#include <linux/rtnetlink.h> |
|
#include <net/devlink.h> |
|
|
|
#include "nfpcore/nfp.h" |
|
#include "nfpcore/nfp_nsp.h" |
|
#include "nfp_app.h" |
|
#include "nfp_main.h" |
|
#include "nfp_port.h" |
|
|
|
static int |
|
nfp_devlink_fill_eth_port(struct nfp_port *port, |
|
struct nfp_eth_table_port *copy) |
|
{ |
|
struct nfp_eth_table_port *eth_port; |
|
|
|
eth_port = __nfp_port_get_eth_port(port); |
|
if (!eth_port) |
|
return -EINVAL; |
|
|
|
memcpy(copy, eth_port, sizeof(*eth_port)); |
|
|
|
return 0; |
|
} |
|
|
|
static int |
|
nfp_devlink_fill_eth_port_from_id(struct nfp_pf *pf, unsigned int port_index, |
|
struct nfp_eth_table_port *copy) |
|
{ |
|
struct nfp_port *port; |
|
|
|
port = nfp_port_from_id(pf, NFP_PORT_PHYS_PORT, port_index); |
|
|
|
return nfp_devlink_fill_eth_port(port, copy); |
|
} |
|
|
|
static int |
|
nfp_devlink_set_lanes(struct nfp_pf *pf, unsigned int idx, unsigned int lanes) |
|
{ |
|
struct nfp_nsp *nsp; |
|
int ret; |
|
|
|
nsp = nfp_eth_config_start(pf->cpp, idx); |
|
if (IS_ERR(nsp)) |
|
return PTR_ERR(nsp); |
|
|
|
ret = __nfp_eth_set_split(nsp, lanes); |
|
if (ret) { |
|
nfp_eth_config_cleanup_end(nsp); |
|
return ret; |
|
} |
|
|
|
ret = nfp_eth_config_commit_end(nsp); |
|
if (ret < 0) |
|
return ret; |
|
if (ret) /* no change */ |
|
return 0; |
|
|
|
return nfp_net_refresh_port_table_sync(pf); |
|
} |
|
|
|
static int |
|
nfp_devlink_port_split(struct devlink *devlink, unsigned int port_index, |
|
unsigned int count, struct netlink_ext_ack *extack) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
struct nfp_eth_table_port eth_port; |
|
unsigned int lanes; |
|
int ret; |
|
|
|
mutex_lock(&pf->lock); |
|
|
|
rtnl_lock(); |
|
ret = nfp_devlink_fill_eth_port_from_id(pf, port_index, ð_port); |
|
rtnl_unlock(); |
|
if (ret) |
|
goto out; |
|
|
|
if (eth_port.port_lanes % count) { |
|
ret = -EINVAL; |
|
goto out; |
|
} |
|
|
|
/* Special case the 100G CXP -> 2x40G split */ |
|
lanes = eth_port.port_lanes / count; |
|
if (eth_port.lanes == 10 && count == 2) |
|
lanes = 8 / count; |
|
|
|
ret = nfp_devlink_set_lanes(pf, eth_port.index, lanes); |
|
out: |
|
mutex_unlock(&pf->lock); |
|
|
|
return ret; |
|
} |
|
|
|
static int |
|
nfp_devlink_port_unsplit(struct devlink *devlink, unsigned int port_index, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
struct nfp_eth_table_port eth_port; |
|
unsigned int lanes; |
|
int ret; |
|
|
|
mutex_lock(&pf->lock); |
|
|
|
rtnl_lock(); |
|
ret = nfp_devlink_fill_eth_port_from_id(pf, port_index, ð_port); |
|
rtnl_unlock(); |
|
if (ret) |
|
goto out; |
|
|
|
if (!eth_port.is_split) { |
|
ret = -EINVAL; |
|
goto out; |
|
} |
|
|
|
/* Special case the 100G CXP -> 2x40G unsplit */ |
|
lanes = eth_port.port_lanes; |
|
if (eth_port.port_lanes == 8) |
|
lanes = 10; |
|
|
|
ret = nfp_devlink_set_lanes(pf, eth_port.index, lanes); |
|
out: |
|
mutex_unlock(&pf->lock); |
|
|
|
return ret; |
|
} |
|
|
|
static int |
|
nfp_devlink_sb_pool_get(struct devlink *devlink, unsigned int sb_index, |
|
u16 pool_index, struct devlink_sb_pool_info *pool_info) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
|
|
return nfp_shared_buf_pool_get(pf, sb_index, pool_index, pool_info); |
|
} |
|
|
|
static int |
|
nfp_devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index, |
|
u16 pool_index, |
|
u32 size, enum devlink_sb_threshold_type threshold_type, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
|
|
return nfp_shared_buf_pool_set(pf, sb_index, pool_index, |
|
size, threshold_type); |
|
} |
|
|
|
static int nfp_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
|
|
return nfp_app_eswitch_mode_get(pf->app, mode); |
|
} |
|
|
|
static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
int ret; |
|
|
|
mutex_lock(&pf->lock); |
|
ret = nfp_app_eswitch_mode_set(pf->app, mode); |
|
mutex_unlock(&pf->lock); |
|
|
|
return ret; |
|
} |
|
|
|
static const struct nfp_devlink_versions_simple { |
|
const char *key; |
|
const char *hwinfo; |
|
} nfp_devlink_versions_hwinfo[] = { |
|
{ DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, "assembly.partno", }, |
|
{ DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, "assembly.revision", }, |
|
{ DEVLINK_INFO_VERSION_GENERIC_BOARD_MANUFACTURE, "assembly.vendor", }, |
|
{ "board.model", /* code name */ "assembly.model", }, |
|
}; |
|
|
|
static int |
|
nfp_devlink_versions_get_hwinfo(struct nfp_pf *pf, struct devlink_info_req *req) |
|
{ |
|
unsigned int i; |
|
int err; |
|
|
|
for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_hwinfo); i++) { |
|
const struct nfp_devlink_versions_simple *info; |
|
const char *val; |
|
|
|
info = &nfp_devlink_versions_hwinfo[i]; |
|
|
|
val = nfp_hwinfo_lookup(pf->hwinfo, info->hwinfo); |
|
if (!val) |
|
continue; |
|
|
|
err = devlink_info_version_fixed_put(req, info->key, val); |
|
if (err) |
|
return err; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static const struct nfp_devlink_versions { |
|
enum nfp_nsp_versions id; |
|
const char *key; |
|
} nfp_devlink_versions_nsp[] = { |
|
{ NFP_VERSIONS_BUNDLE, DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, }, |
|
{ NFP_VERSIONS_BSP, DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, }, |
|
{ NFP_VERSIONS_CPLD, "fw.cpld", }, |
|
{ NFP_VERSIONS_APP, DEVLINK_INFO_VERSION_GENERIC_FW_APP, }, |
|
{ NFP_VERSIONS_UNDI, DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, }, |
|
{ NFP_VERSIONS_NCSI, DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, }, |
|
{ NFP_VERSIONS_CFGR, "chip.init", }, |
|
}; |
|
|
|
static int |
|
nfp_devlink_versions_get_nsp(struct devlink_info_req *req, bool flash, |
|
const u8 *buf, unsigned int size) |
|
{ |
|
unsigned int i; |
|
int err; |
|
|
|
for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_nsp); i++) { |
|
const struct nfp_devlink_versions *info; |
|
const char *version; |
|
|
|
info = &nfp_devlink_versions_nsp[i]; |
|
|
|
version = nfp_nsp_versions_get(info->id, flash, buf, size); |
|
if (IS_ERR(version)) { |
|
if (PTR_ERR(version) == -ENOENT) |
|
continue; |
|
else |
|
return PTR_ERR(version); |
|
} |
|
|
|
if (flash) |
|
err = devlink_info_version_stored_put(req, info->key, |
|
version); |
|
else |
|
err = devlink_info_version_running_put(req, info->key, |
|
version); |
|
if (err) |
|
return err; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int |
|
nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
struct nfp_pf *pf = devlink_priv(devlink); |
|
const char *sn, *vendor, *part; |
|
struct nfp_nsp *nsp; |
|
char *buf = NULL; |
|
int err; |
|
|
|
err = devlink_info_driver_name_put(req, "nfp"); |
|
if (err) |
|
return err; |
|
|
|
vendor = nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor"); |
|
part = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno"); |
|
sn = nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial"); |
|
if (vendor && part && sn) { |
|
char *buf; |
|
|
|
buf = kmalloc(strlen(vendor) + strlen(part) + strlen(sn) + 1, |
|
GFP_KERNEL); |
|
if (!buf) |
|
return -ENOMEM; |
|
|
|
buf[0] = '\0'; |
|
strcat(buf, vendor); |
|
strcat(buf, part); |
|
strcat(buf, sn); |
|
|
|
err = devlink_info_serial_number_put(req, buf); |
|
kfree(buf); |
|
if (err) |
|
return err; |
|
} |
|
|
|
nsp = nfp_nsp_open(pf->cpp); |
|
if (IS_ERR(nsp)) { |
|
NL_SET_ERR_MSG_MOD(extack, "can't access NSP"); |
|
return PTR_ERR(nsp); |
|
} |
|
|
|
if (nfp_nsp_has_versions(nsp)) { |
|
buf = kzalloc(NFP_NSP_VERSION_BUFSZ, GFP_KERNEL); |
|
if (!buf) { |
|
err = -ENOMEM; |
|
goto err_close_nsp; |
|
} |
|
|
|
err = nfp_nsp_versions(nsp, buf, NFP_NSP_VERSION_BUFSZ); |
|
if (err) |
|
goto err_free_buf; |
|
|
|
err = nfp_devlink_versions_get_nsp(req, false, |
|
buf, NFP_NSP_VERSION_BUFSZ); |
|
if (err) |
|
goto err_free_buf; |
|
|
|
err = nfp_devlink_versions_get_nsp(req, true, |
|
buf, NFP_NSP_VERSION_BUFSZ); |
|
if (err) |
|
goto err_free_buf; |
|
|
|
kfree(buf); |
|
} |
|
|
|
nfp_nsp_close(nsp); |
|
|
|
return nfp_devlink_versions_get_hwinfo(pf, req); |
|
|
|
err_free_buf: |
|
kfree(buf); |
|
err_close_nsp: |
|
nfp_nsp_close(nsp); |
|
return err; |
|
} |
|
|
|
static int |
|
nfp_devlink_flash_update(struct devlink *devlink, |
|
struct devlink_flash_update_params *params, |
|
struct netlink_ext_ack *extack) |
|
{ |
|
return nfp_flash_update_common(devlink_priv(devlink), params->fw, extack); |
|
} |
|
|
|
const struct devlink_ops nfp_devlink_ops = { |
|
.port_split = nfp_devlink_port_split, |
|
.port_unsplit = nfp_devlink_port_unsplit, |
|
.sb_pool_get = nfp_devlink_sb_pool_get, |
|
.sb_pool_set = nfp_devlink_sb_pool_set, |
|
.eswitch_mode_get = nfp_devlink_eswitch_mode_get, |
|
.eswitch_mode_set = nfp_devlink_eswitch_mode_set, |
|
.info_get = nfp_devlink_info_get, |
|
.flash_update = nfp_devlink_flash_update, |
|
}; |
|
|
|
int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port) |
|
{ |
|
struct devlink_port_attrs attrs = {}; |
|
struct nfp_eth_table_port eth_port; |
|
struct devlink *devlink; |
|
const u8 *serial; |
|
int serial_len; |
|
int ret; |
|
|
|
rtnl_lock(); |
|
ret = nfp_devlink_fill_eth_port(port, ð_port); |
|
rtnl_unlock(); |
|
if (ret) |
|
return ret; |
|
|
|
attrs.split = eth_port.is_split; |
|
attrs.splittable = !attrs.split; |
|
attrs.lanes = eth_port.port_lanes; |
|
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; |
|
attrs.phys.port_number = eth_port.label_port; |
|
attrs.phys.split_subport_number = eth_port.label_subport; |
|
serial_len = nfp_cpp_serial(port->app->cpp, &serial); |
|
memcpy(attrs.switch_id.id, serial, serial_len); |
|
attrs.switch_id.id_len = serial_len; |
|
devlink_port_attrs_set(&port->dl_port, &attrs); |
|
|
|
devlink = priv_to_devlink(app->pf); |
|
|
|
return devlink_port_register(devlink, &port->dl_port, port->eth_id); |
|
} |
|
|
|
void nfp_devlink_port_unregister(struct nfp_port *port) |
|
{ |
|
devlink_port_unregister(&port->dl_port); |
|
} |
|
|
|
void nfp_devlink_port_type_eth_set(struct nfp_port *port) |
|
{ |
|
devlink_port_type_eth_set(&port->dl_port, port->netdev); |
|
} |
|
|
|
void nfp_devlink_port_type_clear(struct nfp_port *port) |
|
{ |
|
devlink_port_type_clear(&port->dl_port); |
|
} |
|
|
|
struct devlink_port *nfp_devlink_get_devlink_port(struct net_device *netdev) |
|
{ |
|
struct nfp_port *port; |
|
|
|
port = nfp_port_from_netdev(netdev); |
|
if (!port) |
|
return NULL; |
|
|
|
return &port->dl_port; |
|
}
|
|
|