1193 lines · 33.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/module.h>#include <linux/netdevice.h>#include <linux/sfp.h> #include "ionic.h"#include "ionic_bus.h"#include "ionic_lif.h"#include "ionic_ethtool.h"#include "ionic_stats.h" #define IONIC_MAX_RX_COPYBREAK	min(U16_MAX, IONIC_MAX_BUF_LEN) static void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf){	u32 i; 	for (i = 0; i < ionic_num_stats_grps; i++)		ionic_stats_groups[i].get_strings(lif, &buf);} static void ionic_get_stats(struct net_device *netdev,			    struct ethtool_stats *stats, u64 *buf){	struct ionic_lif *lif = netdev_priv(netdev);	u32 i; 	if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))		return; 	memset(buf, 0, stats->n_stats * sizeof(*buf));	for (i = 0; i < ionic_num_stats_grps; i++)		ionic_stats_groups[i].get_values(lif, &buf);} static int ionic_get_stats_count(struct ionic_lif *lif){	int i, num_stats = 0; 	for (i = 0; i < ionic_num_stats_grps; i++)		num_stats += ionic_stats_groups[i].get_count(lif); 	return num_stats;} static int ionic_get_sset_count(struct net_device *netdev, int sset){	struct ionic_lif *lif = netdev_priv(netdev);	int count = 0; 	switch (sset) {	case ETH_SS_STATS:		count = ionic_get_stats_count(lif);		break;	}	return count;} static void ionic_get_strings(struct net_device *netdev,			      u32 sset, u8 *buf){	struct ionic_lif *lif = netdev_priv(netdev); 	switch (sset) {	case ETH_SS_STATS:		ionic_get_stats_strings(lif, buf);		break;	}} static void ionic_get_drvinfo(struct net_device *netdev,			      struct ethtool_drvinfo *drvinfo){	struct ionic_lif *lif = netdev_priv(netdev);	struct ionic *ionic = lif->ionic; 	strscpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver));	strscpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version,		sizeof(drvinfo->fw_version));	strscpy(drvinfo->bus_info, ionic_bus_info(ionic),		sizeof(drvinfo->bus_info));} static int ionic_get_regs_len(struct net_device *netdev){	return (IONIC_DEV_INFO_REG_COUNT + IONIC_DEV_CMD_REG_COUNT) * sizeof(u32);} static void ionic_get_regs(struct net_device *netdev, struct ethtool_regs *regs,			   void *p){	struct ionic_lif *lif = netdev_priv(netdev);	struct ionic_dev *idev;	unsigned int offset;	unsigned int size; 	regs->version = IONIC_DEV_CMD_REG_VERSION; 	idev = &lif->ionic->idev;	if (!idev->dev_info_regs)		return; 	offset = 0;	size = IONIC_DEV_INFO_REG_COUNT * sizeof(u32);	memcpy_fromio(p + offset, lif->ionic->idev.dev_info_regs->words, size); 	offset += size;	size = IONIC_DEV_CMD_REG_COUNT * sizeof(u32);	memcpy_fromio(p + offset, idev->dev_cmd_regs->words, size);} static void ionic_get_link_ext_stats(struct net_device *netdev,				     struct ethtool_link_ext_stats *stats){	struct ionic_lif *lif = netdev_priv(netdev);	struct ionic *ionic = lif->ionic;	u64 link_down_count_total;	u16 link_down_count_fw; 	if (ionic->pdev->is_virtfn)		return; 	if (!ionic->idev.port_info) {		netdev_err_once(netdev, "port_info not initialized\n");		return;	} 	link_down_count_fw =	    le16_to_cpu(ionic->idev.port_info->status.link_down_count);	link_down_count_total = ionic->idev.link_down_count_total +				link_down_count_fw -				ionic->idev.link_down_count_last; 	/* The firmware counter is only 16 bits and can wraparound */	if (link_down_count_fw < ionic->idev.link_down_count_last)		link_down_count_total += BIT(16); 	ionic->idev.link_down_count_last = link_down_count_fw;	ionic->idev.link_down_count_total = link_down_count_total; 	stats->link_down_events = link_down_count_total;} static int ionic_get_link_ksettings(struct net_device *netdev,				    struct ethtool_link_ksettings *ks){	struct ionic_lif *lif = netdev_priv(netdev);	struct ionic_dev *idev = &lif->ionic->idev;	int copper_seen = 0; 	ethtool_link_ksettings_zero_link_mode(ks, supported); 	if (!idev->port_info) {		netdev_err(netdev, "port_info not initialized\n");		return -EOPNOTSUPP;	} 	/* The port_info data is found in a DMA space that the NIC keeps	 * up-to-date, so there's no need to request the data from the	 * NIC, we already have it in our memory space.	 */ 	switch (le16_to_cpu(idev->port_info->status.xcvr.pid)) {		/* Copper */	case IONIC_XCVR_PID_QSFP_100G_CR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     100000baseCR4_Full);		copper_seen++;		break;	case IONIC_XCVR_PID_QSFP_40GBASE_CR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     40000baseCR4_Full);		copper_seen++;		break;	case IONIC_XCVR_PID_SFP_25GBASE_CR_S:	case IONIC_XCVR_PID_SFP_25GBASE_CR_L:	case IONIC_XCVR_PID_SFP_25GBASE_CR_N:		ethtool_link_ksettings_add_link_mode(ks, supported,						     25000baseCR_Full);		copper_seen++;		break;	case IONIC_XCVR_PID_QSFP_50G_CR2_FC:	case IONIC_XCVR_PID_QSFP_50G_CR2:		ethtool_link_ksettings_add_link_mode(ks, supported,						     50000baseCR2_Full);		copper_seen++;		break;	case IONIC_XCVR_PID_QSFP_200G_CR4:		ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseCR4_Full);		copper_seen++;		break;	case IONIC_XCVR_PID_QSFP_400G_CR4:		ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseCR4_Full);		copper_seen++;		break;	case IONIC_XCVR_PID_SFP_10GBASE_AOC:	case IONIC_XCVR_PID_SFP_10GBASE_CU:		ethtool_link_ksettings_add_link_mode(ks, supported,						     10000baseCR_Full);		copper_seen++;		break; 		/* Fibre */	case IONIC_XCVR_PID_QSFP_100G_SR4:	case IONIC_XCVR_PID_QSFP_100G_AOC:		ethtool_link_ksettings_add_link_mode(ks, supported,						     100000baseSR4_Full);		break;	case IONIC_XCVR_PID_QSFP_100G_CWDM4:	case IONIC_XCVR_PID_QSFP_100G_PSM4:	case IONIC_XCVR_PID_QSFP_100G_LR4:	case IONIC_XCVR_PID_QSFP_100G_ER4:	case IONIC_XCVR_PID_QSFP_100G_FR4:	case IONIC_XCVR_PID_QSFP_100G_DR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     100000baseLR4_ER4_Full);		break;	case IONIC_XCVR_PID_QSFP_40GBASE_SR4:	case IONIC_XCVR_PID_QSFP_40GBASE_AOC:		ethtool_link_ksettings_add_link_mode(ks, supported,						     40000baseSR4_Full);		break;	case IONIC_XCVR_PID_QSFP_40GBASE_LR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     40000baseLR4_Full);		break;	case IONIC_XCVR_PID_SFP_25GBASE_SR:	case IONIC_XCVR_PID_SFP_25GBASE_AOC:	case IONIC_XCVR_PID_SFP_25GBASE_ACC:		ethtool_link_ksettings_add_link_mode(ks, supported,						     25000baseSR_Full);		break;	case IONIC_XCVR_PID_QSFP_200G_AOC:	case IONIC_XCVR_PID_QSFP_200G_SR4:	case IONIC_XCVR_PID_QSFP_200G_AEC:		ethtool_link_ksettings_add_link_mode(ks, supported,						     200000baseSR4_Full);		break;	case IONIC_XCVR_PID_QSFP_200G_FR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     200000baseLR4_ER4_FR4_Full);		break;	case IONIC_XCVR_PID_QSFP_200G_DR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     200000baseDR4_Full);		break;	case IONIC_XCVR_PID_QSFP_400G_FR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     400000baseLR4_ER4_FR4_Full);		break;	case IONIC_XCVR_PID_QSFP_400G_DR4:		ethtool_link_ksettings_add_link_mode(ks, supported,						     400000baseDR4_Full);		break;	case IONIC_XCVR_PID_QSFP_400G_SR4:	case IONIC_XCVR_PID_QSFP_400G_AOC:	case IONIC_XCVR_PID_QSFP_400G_AEC:	case IONIC_XCVR_PID_QSFP_400G_LPO:		ethtool_link_ksettings_add_link_mode(ks, supported,						     400000baseSR4_Full);		break;	case IONIC_XCVR_PID_SFP_10GBASE_SR:		ethtool_link_ksettings_add_link_mode(ks, supported,						     10000baseSR_Full);		break;	case IONIC_XCVR_PID_SFP_10GBASE_LR:		ethtool_link_ksettings_add_link_mode(ks, supported,						     10000baseLR_Full);		break;	case IONIC_XCVR_PID_SFP_10GBASE_LRM:		ethtool_link_ksettings_add_link_mode(ks, supported,						     10000baseLRM_Full);		break;	case IONIC_XCVR_PID_SFP_10GBASE_ER:		ethtool_link_ksettings_add_link_mode(ks, supported,						     10000baseER_Full);		break;	case IONIC_XCVR_PID_SFP_10GBASE_T:		ethtool_link_ksettings_add_link_mode(ks, supported,						     10000baseT_Full);		break;	case IONIC_XCVR_PID_SFP_1000BASE_T:		ethtool_link_ksettings_add_link_mode(ks, supported,						     1000baseT_Full);		break;	case IONIC_XCVR_PID_UNKNOWN:		/* This means there's no module plugged in */		break;	default:		dev_dbg_ratelimited(lif->ionic->dev,				    "unknown xcvr type pid=%d / 0x%x\n",				    idev->port_info->status.xcvr.pid,				    idev->port_info->status.xcvr.pid);		break;	} 	linkmode_copy(ks->link_modes.advertising, ks->link_modes.supported); 	ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER);	ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS);	if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_FC)		ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_BASER);	else if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_RS)		ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS); 	ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE);	ethtool_link_ksettings_add_link_mode(ks, supported, Pause); 	if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_COPPER ||	    copper_seen)		ks->base.port = PORT_DA;	else if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_FIBER)		ks->base.port = PORT_FIBRE;	else		ks->base.port = PORT_NONE; 	if (ks->base.port != PORT_NONE) {		ks->base.speed = le32_to_cpu(lif->info->status.link_speed); 		if (le16_to_cpu(lif->info->status.link_status))			ks->base.duplex = DUPLEX_FULL;		else			ks->base.duplex = DUPLEX_UNKNOWN; 		ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); 		if (idev->port_info->config.an_enable) {			ethtool_link_ksettings_add_link_mode(ks, advertising,							     Autoneg);			ks->base.autoneg = AUTONEG_ENABLE;		}	} 	return 0;} static int ionic_set_link_ksettings(struct net_device *netdev,				    const struct ethtool_link_ksettings *ks){	struct ionic_lif *lif = netdev_priv(netdev);	struct ionic_dev *idev = &lif->ionic->idev;	struct ionic *ionic = lif->ionic;	int err = 0; 	if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))		return -EBUSY; 	/* set autoneg */	if (ks->base.autoneg != idev->port_info->config.an_enable) {		mutex_lock(&ionic->dev_cmd_lock);		ionic_dev_cmd_port_autoneg(idev, ks->base.autoneg);		err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);		mutex_unlock(&ionic->dev_cmd_lock);		if (err)			return err;	} 	/* set speed */	if (ks->base.speed != le32_to_cpu(idev->port_info->config.speed)) {		mutex_lock(&ionic->dev_cmd_lock);		ionic_dev_cmd_port_speed(idev, ks->base.speed);		err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);		mutex_unlock(&ionic->dev_cmd_lock);		if (err)			return err;	} 	return 0;} static void ionic_get_pauseparam(struct net_device *netdev,				 struct ethtool_pauseparam *pause){	struct ionic_lif *lif = netdev_priv(netdev);	u8 pause_type; 	pause->autoneg = 0; 	pause_type = lif->ionic->idev.port_info->config.pause_type;	if (pause_type) {		pause->rx_pause = (pause_type & IONIC_PAUSE_F_RX) ? 1 : 0;		pause->tx_pause = (pause_type & IONIC_PAUSE_F_TX) ? 1 : 0;	}} static int ionic_set_pauseparam(struct net_device *netdev,				struct ethtool_pauseparam *pause){	struct ionic_lif *lif = netdev_priv(netdev);	struct ionic *ionic = lif->ionic;	u32 requested_pause;	int err; 	if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))		return -EBUSY; 	if (pause->autoneg)		return -EOPNOTSUPP; 	/* change both at the same time */	requested_pause = IONIC_PORT_PAUSE_TYPE_LINK;	if (pause->rx_pause)		requested_pause |= IONIC_PAUSE_F_RX;	if (pause->tx_pause)		requested_pause |= IONIC_PAUSE_F_TX; 	if (requested_pause == lif->ionic->idev.port_info->config.pause_type)		return 0; 	mutex_lock(&ionic->dev_cmd_lock);	ionic_dev_cmd_port_pause(&lif->ionic->idev, requested_pause);	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);	mutex_unlock(&ionic->dev_cmd_lock);	if (err)		return err; 	return 0;} static int ionic_get_fecparam(struct net_device *netdev,			      struct ethtool_fecparam *fec){	struct ionic_lif *lif = netdev_priv(netdev); 	switch (lif->ionic->idev.port_info->config.fec_type) {	case IONIC_PORT_FEC_TYPE_NONE:		fec->active_fec = ETHTOOL_FEC_OFF;		break;	case IONIC_PORT_FEC_TYPE_RS:		fec->active_fec = ETHTOOL_FEC_RS;		break;	case IONIC_PORT_FEC_TYPE_FC:		fec->active_fec = ETHTOOL_FEC_BASER;		break;	} 	fec->fec = ETHTOOL_FEC_OFF | ETHTOOL_FEC_RS | ETHTOOL_FEC_BASER; 	return 0;} static int ionic_set_fecparam(struct net_device *netdev,			      struct ethtool_fecparam *fec){	struct ionic_lif *lif = netdev_priv(netdev);	u8 fec_type;	int ret = 0; 	if (test_bit(IONIC_LIF_F_FW_RESET, lif->state))		return -EBUSY; 	if (lif->ionic->idev.port_info->config.an_enable) {		netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n");		return -EINVAL;	} 	switch (fec->fec) {	case ETHTOOL_FEC_NONE:		fec_type = IONIC_PORT_FEC_TYPE_NONE;		break;	case ETHTOOL_FEC_OFF:		fec_type = IONIC_PORT_FEC_TYPE_NONE;		break;	case ETHTOOL_FEC_RS:		fec_type = IONIC_PORT_FEC_TYPE_RS;		break;	case ETHTOOL_FEC_BASER:		fec_type = IONIC_PORT_FEC_TYPE_FC;		break;	case ETHTOOL_FEC_AUTO:	default:		netdev_err(netdev, "FEC request 0x%04x not supported\n",			   fec->fec);		return -EINVAL;	} 	if (fec_type != lif->ionic->idev.port_info->config.fec_type) {		mutex_lock(&lif->ionic->dev_cmd_lock);		ionic_dev_cmd_port_fec(&lif->ionic->idev, fec_type);		ret = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT);		mutex_unlock(&lif->ionic->dev_cmd_lock);	} 	return ret;} static int ionic_get_coalesce(struct net_device *netdev,			      struct ethtool_coalesce *coalesce,			      struct kernel_ethtool_coalesce *kernel_coal,			      struct netlink_ext_ack *extack){	struct ionic_lif *lif = netdev_priv(netdev); 	coalesce->tx_coalesce_usecs = lif->tx_coalesce_usecs;	coalesce->rx_coalesce_usecs = lif->rx_coalesce_usecs; 	if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))		coalesce->use_adaptive_tx_coalesce = test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state);

Lines 1–500 of 1193.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t