Merge branch 'nfp-port-speed-and-eeprom-get-set-updates'

Simon Horman says:

====================
nfp: port speed and eeprom get/set updates

this short series is the initial updates for the NFP driver for the v6.1
Kernel. It covers two enhancements:

1. Patches 1/3 and 2/3:
   - Support cases where application firmware does not know port speeds
     a priori by relaying this information from the management firmware
     to the application firmware.

     This allows the existing mechanism, whereby the driver reports port
     speeds to user-space as provided by the application firmware, to work
     in this case.

2. Patch 2/3:
   - Add support for eeprom get and set command
====================

Link: https://lore.kernel.org/r/20220825141223.22346-1-simon.horman@corigine.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2022-08-30 11:29:29 +02:00
commit d287532edf
7 changed files with 295 additions and 25 deletions

View File

@ -65,6 +65,7 @@ struct nfp_dumpspec {
* @num_vfs: Number of SR-IOV VFs enabled
* @fw_loaded: Is the firmware loaded?
* @unload_fw_on_remove:Do we need to unload firmware on driver removal?
* @sp_indiff: Is the firmware indifferent to physical port speed?
* @ctrl_vnic: Pointer to the control vNIC if available
* @mip: MIP handle
* @rtbl: RTsym table
@ -114,6 +115,7 @@ struct nfp_pf {
bool fw_loaded;
bool unload_fw_on_remove;
bool sp_indiff;
struct nfp_net *ctrl_vnic;
@ -190,4 +192,7 @@ int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb,
int nfp_devlink_params_register(struct nfp_pf *pf);
void nfp_devlink_params_unregister(struct nfp_pf *pf);
unsigned int nfp_net_lr2speed(unsigned int linkrate);
unsigned int nfp_net_speed2lr(unsigned int speed);
#endif /* NFP_MAIN_H */

View File

@ -474,19 +474,22 @@ static void nfp_net_read_link_status(struct nfp_net *nn)
{
unsigned long flags;
bool link_up;
u32 sts;
u16 sts;
spin_lock_irqsave(&nn->link_status_lock, flags);
sts = nn_readl(nn, NFP_NET_CFG_STS);
sts = nn_readw(nn, NFP_NET_CFG_STS);
link_up = !!(sts & NFP_NET_CFG_STS_LINK);
if (nn->link_up == link_up)
goto out;
nn->link_up = link_up;
if (nn->port)
if (nn->port) {
set_bit(NFP_PORT_CHANGED, &nn->port->flags);
if (nn->port->link_cb)
nn->port->link_cb(nn->port);
}
if (nn->link_up) {
netif_carrier_on(nn->dp.netdev);

View File

@ -148,6 +148,14 @@ int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,
true))
return -EINVAL;
break;
case NFP_NET_CFG_TLV_TYPE_SP_INDIFF:
if (length) {
dev_err(dev, "Unexpected len of SP_INDIFF TLV:%u\n", length);
return -EINVAL;
}
caps->sp_indiff = true;
break;
default:
if (!FIELD_GET(NFP_NET_CFG_TLV_HEADER_REQUIRED, hdr))
break;

View File

@ -193,6 +193,10 @@
#define NFP_NET_CFG_STS_LINK_RATE_40G 5
#define NFP_NET_CFG_STS_LINK_RATE_50G 6
#define NFP_NET_CFG_STS_LINK_RATE_100G 7
/* NSP Link rate is a 16-bit word. It's determined by NSP and
* written to CFG BAR by NFP driver.
*/
#define NFP_NET_CFG_STS_NSP_LINK_RATE 0x0036
#define NFP_NET_CFG_CAP 0x0038
#define NFP_NET_CFG_MAX_TXRINGS 0x003c
#define NFP_NET_CFG_MAX_RXRINGS 0x0040
@ -488,6 +492,10 @@
* %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN:
* Same as %NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS, but crypto TLS does stream scan
* RX sync, rather than kernel-assisted sync.
*
* %NFP_NET_CFG_TLV_TYPE_SP_INDIFF:
* Empty, indicate the firmware is indifferent to port speed. Then no need to
* reload driver and firmware when port speed is changed.
*/
#define NFP_NET_CFG_TLV_TYPE_UNKNOWN 0
#define NFP_NET_CFG_TLV_TYPE_RESERVED 1
@ -501,6 +509,7 @@
#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS 11 /* see crypto/fw.h */
#define NFP_NET_CFG_TLV_TYPE_VNIC_STATS 12
#define NFP_NET_CFG_TLV_TYPE_CRYPTO_OPS_RX_SCAN 13
#define NFP_NET_CFG_TLV_TYPE_SP_INDIFF 14
struct device;
@ -515,6 +524,7 @@ struct device;
* @vnic_stats_off: offset of vNIC stats area
* @vnic_stats_cnt: number of vNIC stats
* @tls_resync_ss: TLS resync will be performed via stream scan
* @sp_indiff: Firmware is indifferent to port speed
*/
struct nfp_net_tlv_caps {
u32 me_freq_mhz;
@ -527,6 +537,7 @@ struct nfp_net_tlv_caps {
unsigned int vnic_stats_off;
unsigned int vnic_stats_cnt;
unsigned int tls_resync_ss:1;
unsigned int sp_indiff:1;
};
int nfp_net_tlv_caps_parse(struct device *dev, u8 __iomem *ctrl_mem,

View File

@ -273,20 +273,11 @@ static int
nfp_net_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
static const u32 ls_to_ethtool[] = {
[NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0,
[NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN,
[NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000,
[NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000,
[NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000,
[NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000,
[NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000,
[NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000,
};
struct nfp_eth_table_port *eth_port;
struct nfp_port *port;
struct nfp_net *nn;
u32 sts, ls;
unsigned int speed;
u16 sts;
/* Init to unknowns */
ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
@ -319,18 +310,15 @@ nfp_net_get_link_ksettings(struct net_device *netdev,
return -EOPNOTSUPP;
nn = netdev_priv(netdev);
sts = nn_readl(nn, NFP_NET_CFG_STS);
ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts);
if (ls == NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED)
sts = nn_readw(nn, NFP_NET_CFG_STS);
speed = nfp_net_lr2speed(FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts));
if (!speed)
return -EOPNOTSUPP;
if (ls == NFP_NET_CFG_STS_LINK_RATE_UNKNOWN ||
ls >= ARRAY_SIZE(ls_to_ethtool))
return 0;
cmd->base.speed = ls_to_ethtool[ls];
cmd->base.duplex = DUPLEX_FULL;
if (speed != SPEED_UNKNOWN) {
cmd->base.speed = speed;
cmd->base.duplex = DUPLEX_FULL;
}
return 0;
}
@ -1676,6 +1664,160 @@ static int nfp_net_set_phys_id(struct net_device *netdev,
return err;
}
#define NFP_EEPROM_LEN ETH_ALEN
static int
nfp_net_get_eeprom_len(struct net_device *netdev)
{
struct nfp_eth_table_port *eth_port;
struct nfp_port *port;
port = nfp_port_from_netdev(netdev);
eth_port = __nfp_port_get_eth_port(port);
if (!eth_port)
return 0;
return NFP_EEPROM_LEN;
}
static int
nfp_net_get_nsp_hwindex(struct net_device *netdev,
struct nfp_nsp **nspptr,
u32 *index)
{
struct nfp_eth_table_port *eth_port;
struct nfp_port *port;
struct nfp_nsp *nsp;
int err;
port = nfp_port_from_netdev(netdev);
eth_port = __nfp_port_get_eth_port(port);
if (!eth_port)
return -EOPNOTSUPP;
nsp = nfp_nsp_open(port->app->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
netdev_err(netdev, "Failed to access the NSP: %d\n", err);
return err;
}
if (!nfp_nsp_has_hwinfo_lookup(nsp)) {
netdev_err(netdev, "NSP doesn't support PF MAC generation\n");
nfp_nsp_close(nsp);
return -EOPNOTSUPP;
}
*nspptr = nsp;
*index = eth_port->eth_index;
return 0;
}
static int
nfp_net_get_port_mac_by_hwinfo(struct net_device *netdev,
u8 *mac_addr)
{
char hwinfo[32] = {};
struct nfp_nsp *nsp;
u32 index;
int err;
err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index);
if (err)
return err;
snprintf(hwinfo, sizeof(hwinfo), "eth%u.mac", index);
err = nfp_nsp_hwinfo_lookup(nsp, hwinfo, sizeof(hwinfo));
nfp_nsp_close(nsp);
if (err) {
netdev_err(netdev, "Reading persistent MAC address failed: %d\n",
err);
return -EOPNOTSUPP;
}
if (sscanf(hwinfo, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&mac_addr[0], &mac_addr[1], &mac_addr[2],
&mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) {
netdev_err(netdev, "Can't parse persistent MAC address (%s)\n",
hwinfo);
return -EOPNOTSUPP;
}
return 0;
}
static int
nfp_net_set_port_mac_by_hwinfo(struct net_device *netdev,
u8 *mac_addr)
{
char hwinfo[32] = {};
struct nfp_nsp *nsp;
u32 index;
int err;
err = nfp_net_get_nsp_hwindex(netdev, &nsp, &index);
if (err)
return err;
snprintf(hwinfo, sizeof(hwinfo),
"eth%u.mac=%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
index, mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
mac_addr[4], mac_addr[5]);
err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
nfp_nsp_close(nsp);
if (err) {
netdev_err(netdev, "HWinfo set failed: %d, hwinfo: %s\n",
err, hwinfo);
return -EOPNOTSUPP;
}
return 0;
}
static int
nfp_net_get_eeprom(struct net_device *netdev,
struct ethtool_eeprom *eeprom, u8 *bytes)
{
struct nfp_net *nn = netdev_priv(netdev);
u8 buf[NFP_EEPROM_LEN] = {};
if (eeprom->len == 0)
return -EINVAL;
if (nfp_net_get_port_mac_by_hwinfo(netdev, buf))
return -EOPNOTSUPP;
eeprom->magic = nn->pdev->vendor | (nn->pdev->device << 16);
memcpy(bytes, buf + eeprom->offset, eeprom->len);
return 0;
}
static int
nfp_net_set_eeprom(struct net_device *netdev,
struct ethtool_eeprom *eeprom, u8 *bytes)
{
struct nfp_net *nn = netdev_priv(netdev);
u8 buf[NFP_EEPROM_LEN] = {};
if (eeprom->len == 0)
return -EINVAL;
if (eeprom->magic != (nn->pdev->vendor | nn->pdev->device << 16))
return -EINVAL;
if (nfp_net_get_port_mac_by_hwinfo(netdev, buf))
return -EOPNOTSUPP;
memcpy(buf + eeprom->offset, bytes, eeprom->len);
if (nfp_net_set_port_mac_by_hwinfo(netdev, buf))
return -EOPNOTSUPP;
return 0;
}
static const struct ethtool_ops nfp_net_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |
@ -1699,6 +1841,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
.set_dump = nfp_app_set_dump,
.get_dump_flag = nfp_app_get_dump_flag,
.get_dump_data = nfp_app_get_dump_data,
.get_eeprom_len = nfp_net_get_eeprom_len,
.get_eeprom = nfp_net_get_eeprom,
.set_eeprom = nfp_net_set_eeprom,
.get_module_info = nfp_port_get_module_info,
.get_module_eeprom = nfp_port_get_module_eeprom,
.get_coalesce = nfp_net_get_coalesce,

View File

@ -202,7 +202,11 @@ nfp_net_pf_alloc_vnics(struct nfp_pf *pf, void __iomem *ctrl_bar,
goto err_free_prev;
}
if (nn->port)
nn->port->link_cb = nfp_net_refresh_port_table;
ctrl_bar += NFP_PF_CSR_SLICE_SIZE;
pf->sp_indiff |= nn->tlv_caps.sp_indiff;
/* Kill the vNIC if app init marked it as invalid */
if (nn->port && nn->port->type == NFP_PORT_INVALID)
@ -304,6 +308,37 @@ err_prev_deinit:
return err;
}
static int nfp_net_pf_cfg_nsp(struct nfp_pf *pf, bool sp_indiff)
{
struct nfp_nsp *nsp;
char hwinfo[32];
int err;
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
return err;
}
snprintf(hwinfo, sizeof(hwinfo), "sp_indiff=%d", sp_indiff);
err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
if (err)
nfp_warn(pf->cpp, "HWinfo(sp_indiff=%d) set failed: %d\n", sp_indiff, err);
nfp_nsp_close(nsp);
return err;
}
static int nfp_net_pf_init_nsp(struct nfp_pf *pf)
{
return nfp_net_pf_cfg_nsp(pf, pf->sp_indiff);
}
static void nfp_net_pf_clean_nsp(struct nfp_pf *pf)
{
(void)nfp_net_pf_cfg_nsp(pf, false);
}
static int
nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)
{
@ -315,6 +350,8 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride)
if (IS_ERR(pf->app))
return PTR_ERR(pf->app);
pf->sp_indiff |= pf->app->type->id == NFP_APP_FLOWER_NIC;
devl_lock(devlink);
err = nfp_app_init(pf->app);
devl_unlock(devlink);
@ -523,6 +560,57 @@ err_unmap_ctrl:
return err;
}
static const unsigned int lr_to_speed[] = {
[NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0,
[NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN,
[NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000,
[NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000,
[NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000,
[NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000,
[NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000,
[NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000,
};
unsigned int nfp_net_lr2speed(unsigned int linkrate)
{
if (linkrate < ARRAY_SIZE(lr_to_speed))
return lr_to_speed[linkrate];
return SPEED_UNKNOWN;
}
unsigned int nfp_net_speed2lr(unsigned int speed)
{
int i;
for (i = 0; i < ARRAY_SIZE(lr_to_speed); i++) {
if (speed == lr_to_speed[i])
return i;
}
return NFP_NET_CFG_STS_LINK_RATE_UNKNOWN;
}
static void nfp_net_notify_port_speed(struct nfp_port *port)
{
struct net_device *netdev = port->netdev;
struct nfp_net *nn;
u16 sts;
if (!nfp_netdev_is_nfp_net(netdev))
return;
nn = netdev_priv(netdev);
sts = nn_readw(nn, NFP_NET_CFG_STS);
if (!(sts & NFP_NET_CFG_STS_LINK)) {
nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, NFP_NET_CFG_STS_LINK_RATE_UNKNOWN);
return;
}
nn_writew(nn, NFP_NET_CFG_STS_NSP_LINK_RATE, nfp_net_speed2lr(port->eth_port->speed));
}
static int
nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,
struct nfp_eth_table *eth_table)
@ -544,6 +632,7 @@ nfp_net_eth_port_update(struct nfp_cpp *cpp, struct nfp_port *port,
}
memcpy(port->eth_port, eth_port, sizeof(*eth_port));
nfp_net_notify_port_speed(port);
return 0;
}
@ -725,10 +814,14 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
if (err)
goto err_clean_ddir;
err = nfp_net_pf_alloc_irqs(pf);
err = nfp_net_pf_init_nsp(pf);
if (err)
goto err_free_vnics;
err = nfp_net_pf_alloc_irqs(pf);
if (err)
goto err_clean_nsp;
err = nfp_net_pf_app_start(pf);
if (err)
goto err_free_irqs;
@ -746,6 +839,8 @@ err_stop_app:
nfp_net_pf_app_stop(pf);
err_free_irqs:
nfp_net_pf_free_irqs(pf);
err_clean_nsp:
nfp_net_pf_clean_nsp(pf);
err_free_vnics:
nfp_net_pf_free_vnics(pf);
err_clean_ddir:
@ -776,6 +871,7 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
nfp_net_pf_free_vnic(pf, nn);
}
nfp_net_pf_clean_nsp(pf);
nfp_net_pf_app_stop(pf);
/* stop app first, to avoid double free of ctrl vNIC's ddir */
nfp_net_debugfs_dir_clean(&pf->ddir);

View File

@ -46,6 +46,7 @@ enum nfp_port_flags {
* @tc_offload_cnt: number of active TC offloads, how offloads are counted
* is not defined, use as a boolean
* @app: backpointer to the app structure
* @link_cb: callback when link status changed
* @dl_port: devlink port structure
* @eth_id: for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme
* @eth_forced: for %NFP_PORT_PHYS_PORT port is forced UP or DOWN, don't change
@ -66,6 +67,7 @@ struct nfp_port {
unsigned long tc_offload_cnt;
struct nfp_app *app;
void (*link_cb)(struct nfp_port *port);
struct devlink_port dl_port;