Commit 89304496 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'hinic-add-some-ethtool-ops-support'



Luo bin says:

====================
hinic: add some ethtool ops support

patch #1: support to set and get pause params with
          "ethtool -A/a" cmd
patch #2: support to set and get irq coalesce params with
          "ethtool -C/c" cmd
patch #3: support to do self test with "ethtool -t" cmd
patch #4: support to identify physical device with "ethtool -p" cmd
patch #5: support to get eeprom information with "ethtool -m" cmd
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 491f14db 2ac84cd1
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -20,11 +20,14 @@

#define HINIC_DRV_NAME          "hinic"

#define LP_PKT_CNT		64

enum hinic_flags {
	HINIC_LINK_UP = BIT(0),
	HINIC_INTF_UP = BIT(1),
	HINIC_RSS_ENABLE = BIT(2),
	HINIC_LINK_DOWN = BIT(3),
	HINIC_LP_TEST = BIT(4),
};

struct hinic_rx_mode_work {
@@ -49,6 +52,12 @@ enum hinic_rss_hash_type {
	HINIC_RSS_HASH_ENGINE_TYPE_MAX,
};

struct hinic_intr_coal_info {
	u8	pending_limt;
	u8	coalesce_timer_cfg;
	u8	resend_timer_cfg;
};

struct hinic_dev {
	struct net_device               *netdev;
	struct hinic_hwdev              *hwdev;
@@ -82,7 +91,12 @@ struct hinic_dev {
	struct hinic_rss_type		rss_type;
	u8				*rss_hkey_user;
	s32				*rss_indir_user;
	struct hinic_intr_coal_info	*rx_intr_coalesce;
	struct hinic_intr_coal_info	*tx_intr_coalesce;
	struct hinic_sriov_info sriov_info;
	int				lb_test_rx_idx;
	int				lb_pkt_len;
	u8				*lb_test_rx_buf;
};

#endif
+599 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <linux/if_vlan.h>
#include <linux/ethtool.h>
#include <linux/vmalloc.h>
#include <linux/sfp.h>

#include "hinic_hw_qp.h"
#include "hinic_hw_dev.h"
@@ -49,6 +50,13 @@
#define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode)	\
				((ecmd)->advertising |= ADVERTISED_##mode)

#define COALESCE_PENDING_LIMIT_UNIT	8
#define	COALESCE_TIMER_CFG_UNIT		9
#define COALESCE_ALL_QUEUE		0xFFFF
#define COALESCE_MAX_PENDING_LIMIT	(255 * COALESCE_PENDING_LIMIT_UNIT)
#define COALESCE_MAX_TIMER_CFG		(255 * COALESCE_TIMER_CFG_UNIT)
#define OBJ_STR_MAX_LEN			32

struct hw2ethtool_link_mode {
	enum ethtool_link_mode_bit_indices link_mode_bit;
	u32 speed;
@@ -126,6 +134,16 @@ static struct hw2ethtool_link_mode
	},
};

#define LP_DEFAULT_TIME                 5 /* seconds */
#define LP_PKT_LEN                      1514

#define PORT_DOWN_ERR_IDX		0
enum diag_test_index {
	INTERNAL_LP_TEST = 0,
	EXTERNAL_LP_TEST = 1,
	DIAG_TEST_MAX = 2,
};

static void set_link_speed(struct ethtool_link_ksettings *link_ksettings,
			   enum hinic_speed speed)
{
@@ -613,6 +631,273 @@ static int hinic_set_ringparam(struct net_device *netdev,

	return 0;
}

static int __hinic_get_coalesce(struct net_device *netdev,
				struct ethtool_coalesce *coal, u16 queue)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	struct hinic_intr_coal_info *rx_intr_coal_info;
	struct hinic_intr_coal_info *tx_intr_coal_info;

	if (queue == COALESCE_ALL_QUEUE) {
		/* get tx/rx irq0 as default parameters */
		rx_intr_coal_info = &nic_dev->rx_intr_coalesce[0];
		tx_intr_coal_info = &nic_dev->tx_intr_coalesce[0];
	} else {
		if (queue >= nic_dev->num_qps) {
			netif_err(nic_dev, drv, netdev,
				  "Invalid queue_id: %d\n", queue);
			return -EINVAL;
		}
		rx_intr_coal_info = &nic_dev->rx_intr_coalesce[queue];
		tx_intr_coal_info = &nic_dev->tx_intr_coalesce[queue];
	}

	/* coalesce_timer is in unit of 9us */
	coal->rx_coalesce_usecs = rx_intr_coal_info->coalesce_timer_cfg *
			COALESCE_TIMER_CFG_UNIT;
	/* coalesced_frames is in unit of 8 */
	coal->rx_max_coalesced_frames = rx_intr_coal_info->pending_limt *
			COALESCE_PENDING_LIMIT_UNIT;
	coal->tx_coalesce_usecs = tx_intr_coal_info->coalesce_timer_cfg *
			COALESCE_TIMER_CFG_UNIT;
	coal->tx_max_coalesced_frames = tx_intr_coal_info->pending_limt *
			COALESCE_PENDING_LIMIT_UNIT;

	return 0;
}

static int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal)
{
	if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
	    coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT ||
	    coal->tx_coalesce_usecs > COALESCE_MAX_TIMER_CFG ||
	    coal->tx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT)
		return -ERANGE;

	return 0;
}

static int set_queue_coalesce(struct hinic_dev *nic_dev, u16 q_id,
			      struct hinic_intr_coal_info *coal,
			      bool set_rx_coal)
{
	struct hinic_intr_coal_info *intr_coal = NULL;
	struct hinic_msix_config interrupt_info = {0};
	struct net_device *netdev = nic_dev->netdev;
	u16 msix_idx;
	int err;

	intr_coal = set_rx_coal ? &nic_dev->rx_intr_coalesce[q_id] :
		    &nic_dev->tx_intr_coalesce[q_id];

	intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg;
	intr_coal->pending_limt = coal->pending_limt;

	/* netdev not running or qp not in using,
	 * don't need to set coalesce to hw
	 */
	if (!(nic_dev->flags & HINIC_INTF_UP) ||
	    q_id >= nic_dev->num_qps)
		return 0;

	msix_idx = set_rx_coal ? nic_dev->rxqs[q_id].rq->msix_entry :
		   nic_dev->txqs[q_id].sq->msix_entry;
	interrupt_info.msix_index = msix_idx;
	interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg;
	interrupt_info.pending_cnt = intr_coal->pending_limt;
	interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg;

	err = hinic_set_interrupt_cfg(nic_dev->hwdev, &interrupt_info);
	if (err)
		netif_warn(nic_dev, drv, netdev,
			   "Failed to set %s queue%d coalesce",
			   set_rx_coal ? "rx" : "tx", q_id);

	return err;
}

static int __set_hw_coal_param(struct hinic_dev *nic_dev,
			       struct hinic_intr_coal_info *intr_coal,
			       u16 queue, bool set_rx_coal)
{
	int err;
	u16 i;

	if (queue == COALESCE_ALL_QUEUE) {
		for (i = 0; i < nic_dev->max_qps; i++) {
			err = set_queue_coalesce(nic_dev, i, intr_coal,
						 set_rx_coal);
			if (err)
				return err;
		}
	} else {
		if (queue >= nic_dev->num_qps) {
			netif_err(nic_dev, drv, nic_dev->netdev,
				  "Invalid queue_id: %d\n", queue);
			return -EINVAL;
		}
		err = set_queue_coalesce(nic_dev, queue, intr_coal,
					 set_rx_coal);
		if (err)
			return err;
	}

	return 0;
}

static int __hinic_set_coalesce(struct net_device *netdev,
				struct ethtool_coalesce *coal, u16 queue)
{
	struct hinic_intr_coal_info *ori_rx_intr_coal = NULL;
	struct hinic_intr_coal_info *ori_tx_intr_coal = NULL;
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	struct hinic_intr_coal_info rx_intr_coal = {0};
	struct hinic_intr_coal_info tx_intr_coal = {0};
	char obj_str[OBJ_STR_MAX_LEN] = {0};
	bool set_rx_coal = false;
	bool set_tx_coal = false;
	int err;

	err = is_coalesce_exceed_limit(coal);
	if (err)
		return err;

	if (coal->rx_coalesce_usecs || coal->rx_max_coalesced_frames) {
		rx_intr_coal.coalesce_timer_cfg =
		(u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
		rx_intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames /
				COALESCE_PENDING_LIMIT_UNIT);
		set_rx_coal = true;
	}

	if (coal->tx_coalesce_usecs || coal->tx_max_coalesced_frames) {
		tx_intr_coal.coalesce_timer_cfg =
		(u8)(coal->tx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT);
		tx_intr_coal.pending_limt = (u8)(coal->tx_max_coalesced_frames /
		COALESCE_PENDING_LIMIT_UNIT);
		set_tx_coal = true;
	}

	if (queue == COALESCE_ALL_QUEUE) {
		ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[0];
		ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[0];
		err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for netdev");
	} else {
		ori_rx_intr_coal = &nic_dev->rx_intr_coalesce[queue];
		ori_tx_intr_coal = &nic_dev->tx_intr_coalesce[queue];
		err = snprintf(obj_str, OBJ_STR_MAX_LEN, "for queue %d", queue);
	}
	if (err <= 0 || err >= OBJ_STR_MAX_LEN) {
		netif_err(nic_dev, drv, netdev, "Failed to snprintf string, function return(%d) and dest_len(%d)\n",
			  err, OBJ_STR_MAX_LEN);
		return -EFAULT;
	}

	/* setting coalesce timer or pending limit to zero will disable
	 * coalesce
	 */
	if (set_rx_coal && (!rx_intr_coal.coalesce_timer_cfg ||
			    !rx_intr_coal.pending_limt))
		netif_warn(nic_dev, drv, netdev, "RX coalesce will be disabled\n");
	if (set_tx_coal && (!tx_intr_coal.coalesce_timer_cfg ||
			    !tx_intr_coal.pending_limt))
		netif_warn(nic_dev, drv, netdev, "TX coalesce will be disabled\n");

	if (set_rx_coal) {
		err = __set_hw_coal_param(nic_dev, &rx_intr_coal, queue, true);
		if (err)
			return err;
	}
	if (set_tx_coal) {
		err = __set_hw_coal_param(nic_dev, &tx_intr_coal, queue, false);
		if (err)
			return err;
	}
	return 0;
}

static int hinic_get_coalesce(struct net_device *netdev,
			      struct ethtool_coalesce *coal)
{
	return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
}

static int hinic_set_coalesce(struct net_device *netdev,
			      struct ethtool_coalesce *coal)
{
	return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE);
}

static int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue,
					struct ethtool_coalesce *coal)
{
	return __hinic_get_coalesce(netdev, coal, queue);
}

static int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue,
					struct ethtool_coalesce *coal)
{
	return __hinic_set_coalesce(netdev, coal, queue);
}

static void hinic_get_pauseparam(struct net_device *netdev,
				 struct ethtool_pauseparam *pause)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	struct hinic_pause_config pause_info = {0};
	struct hinic_nic_cfg *nic_cfg;
	int err;

	nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg;

	err = hinic_get_hw_pause_info(nic_dev->hwdev, &pause_info);
	if (!err) {
		pause->autoneg = pause_info.auto_neg;
		if (nic_cfg->pause_set || !pause_info.auto_neg) {
			pause->rx_pause = nic_cfg->rx_pause;
			pause->tx_pause = nic_cfg->tx_pause;
		} else {
			pause->rx_pause = pause_info.rx_pause;
			pause->tx_pause = pause_info.tx_pause;
		}
	}
}

static int hinic_set_pauseparam(struct net_device *netdev,
				struct ethtool_pauseparam *pause)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	struct hinic_pause_config pause_info = {0};
	struct hinic_port_cap port_cap = {0};
	int err;

	err = hinic_port_get_cap(nic_dev, &port_cap);
	if (err)
		return -EIO;

	if (pause->autoneg != port_cap.autoneg_state)
		return -EOPNOTSUPP;

	pause_info.auto_neg = pause->autoneg;
	pause_info.rx_pause = pause->rx_pause;
	pause_info.tx_pause = pause->tx_pause;

	mutex_lock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex);
	err = hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info);
	if (err) {
		mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex);
		return err;
	}
	nic_dev->hwdev->func_to_io.nic_cfg.pause_set = true;
	nic_dev->hwdev->func_to_io.nic_cfg.auto_neg = pause->autoneg;
	nic_dev->hwdev->func_to_io.nic_cfg.rx_pause = pause->rx_pause;
	nic_dev->hwdev->func_to_io.nic_cfg.tx_pause = pause->tx_pause;
	mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex);

	return 0;
}

static void hinic_get_channels(struct net_device *netdev,
			       struct ethtool_channels *channels)
{
@@ -970,6 +1255,11 @@ static struct hinic_stats hinic_function_stats[] = {
	HINIC_FUNC_STAT(rx_err_vport),
};

static char hinic_test_strings[][ETH_GSTRING_LEN] = {
	"Internal lb test  (on/offline)",
	"External lb test (external_lb)",
};

#define HINIC_PORT_STAT(_stat_item) { \
	.name = #_stat_item, \
	.size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \
@@ -1179,6 +1469,8 @@ static int hinic_get_sset_count(struct net_device *netdev, int sset)
	int count, q_num;

	switch (sset) {
	case ETH_SS_TEST:
		return ARRAY_LEN(hinic_test_strings);
	case ETH_SS_STATS:
		q_num = nic_dev->num_qps;
		count = ARRAY_LEN(hinic_function_stats) +
@@ -1201,6 +1493,9 @@ static void hinic_get_strings(struct net_device *netdev,
	u16 i, j;

	switch (stringset) {
	case ETH_SS_TEST:
		memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings));
		return;
	case ETH_SS_STATS:
		for (i = 0; i < ARRAY_LEN(hinic_function_stats); i++) {
			memcpy(p, hinic_function_stats[i].name,
@@ -1234,13 +1529,311 @@ static void hinic_get_strings(struct net_device *netdev,
	}
}

static int hinic_run_lp_test(struct hinic_dev *nic_dev, u32 test_time)
{
	u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf;
	struct net_device *netdev = nic_dev->netdev;
	struct sk_buff *skb_tmp = NULL;
	struct sk_buff *skb = NULL;
	u32 cnt = test_time * 5;
	u8 *test_data = NULL;
	u32 i;
	u8 j;

	skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC);
	if (!skb_tmp)
		return -ENOMEM;

	test_data = __skb_put(skb_tmp, LP_PKT_LEN);

	memset(test_data, 0xFF, 2 * ETH_ALEN);
	test_data[ETH_ALEN] = 0xFE;
	test_data[2 * ETH_ALEN] = 0x08;
	test_data[2 * ETH_ALEN + 1] = 0x0;

	for (i = ETH_HLEN; i < LP_PKT_LEN; i++)
		test_data[i] = i & 0xFF;

	skb_tmp->queue_mapping = 0;
	skb_tmp->ip_summed = CHECKSUM_COMPLETE;
	skb_tmp->dev = netdev;

	for (i = 0; i < cnt; i++) {
		nic_dev->lb_test_rx_idx = 0;
		memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN);

		for (j = 0; j < LP_PKT_CNT; j++) {
			skb = pskb_copy(skb_tmp, GFP_ATOMIC);
			if (!skb) {
				dev_kfree_skb_any(skb_tmp);
				netif_err(nic_dev, drv, netdev,
					  "Copy skb failed for loopback test\n");
				return -ENOMEM;
			}

			/* mark index for every pkt */
			skb->data[LP_PKT_LEN - 1] = j;

			if (hinic_lb_xmit_frame(skb, netdev)) {
				dev_kfree_skb_any(skb);
				dev_kfree_skb_any(skb_tmp);
				netif_err(nic_dev, drv, netdev,
					  "Xmit pkt failed for loopback test\n");
				return -EBUSY;
			}
		}

		/* wait till all pkts received to RX buffer */
		msleep(200);

		for (j = 0; j < LP_PKT_CNT; j++) {
			if (memcmp(lb_test_rx_buf + j * LP_PKT_LEN,
				   skb_tmp->data, LP_PKT_LEN - 1) ||
			    (*(lb_test_rx_buf + j * LP_PKT_LEN +
			     LP_PKT_LEN - 1) != j)) {
				dev_kfree_skb_any(skb_tmp);
				netif_err(nic_dev, drv, netdev,
					  "Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n",
					  j + i * LP_PKT_CNT,
					  LP_PKT_LEN - 1,
					  *(lb_test_rx_buf + j * LP_PKT_LEN +
					    LP_PKT_LEN - 1));
				return -EIO;
			}
		}
	}

	dev_kfree_skb_any(skb_tmp);
	return 0;
}

static int do_lp_test(struct hinic_dev *nic_dev, u32 flags, u32 test_time,
		      enum diag_test_index *test_index)
{
	struct net_device *netdev = nic_dev->netdev;
	u8 *lb_test_rx_buf = NULL;
	int err = 0;

	if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) {
		*test_index = INTERNAL_LP_TEST;
		if (hinic_set_loopback_mode(nic_dev->hwdev,
					    HINIC_INTERNAL_LP_MODE, true)) {
			netif_err(nic_dev, drv, netdev,
				  "Failed to set port loopback mode before loopback test\n");
			return -EIO;
		}
	} else {
		*test_index = EXTERNAL_LP_TEST;
	}

	lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN);
	if (!lb_test_rx_buf) {
		err = -ENOMEM;
	} else {
		nic_dev->lb_test_rx_buf = lb_test_rx_buf;
		nic_dev->lb_pkt_len = LP_PKT_LEN;
		nic_dev->flags |= HINIC_LP_TEST;
		err = hinic_run_lp_test(nic_dev, test_time);
		nic_dev->flags &= ~HINIC_LP_TEST;
		msleep(100);
		vfree(lb_test_rx_buf);
		nic_dev->lb_test_rx_buf = NULL;
	}

	if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) {
		if (hinic_set_loopback_mode(nic_dev->hwdev,
					    HINIC_INTERNAL_LP_MODE, false)) {
			netif_err(nic_dev, drv, netdev,
				  "Failed to cancel port loopback mode after loopback test\n");
			err = -EIO;
		}
	}

	return err;
}

static void hinic_diag_test(struct net_device *netdev,
			    struct ethtool_test *eth_test, u64 *data)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	enum hinic_port_link_state link_state;
	enum diag_test_index test_index = 0;
	int err = 0;

	memset(data, 0, DIAG_TEST_MAX * sizeof(u64));

	/* don't support loopback test when netdev is closed. */
	if (!(nic_dev->flags & HINIC_INTF_UP)) {
		netif_err(nic_dev, drv, netdev,
			  "Do not support loopback test when netdev is closed\n");
		eth_test->flags |= ETH_TEST_FL_FAILED;
		data[PORT_DOWN_ERR_IDX] = 1;
		return;
	}

	netif_carrier_off(netdev);

	err = do_lp_test(nic_dev, eth_test->flags, LP_DEFAULT_TIME,
			 &test_index);
	if (err) {
		eth_test->flags |= ETH_TEST_FL_FAILED;
		data[test_index] = 1;
	}

	err = hinic_port_link_state(nic_dev, &link_state);
	if (!err && link_state == HINIC_LINK_STATE_UP)
		netif_carrier_on(netdev);
}

static int hinic_set_phys_id(struct net_device *netdev,
			     enum ethtool_phys_id_state state)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	int err = 0;
	u8 port;

	port = nic_dev->hwdev->port_id;

	switch (state) {
	case ETHTOOL_ID_ACTIVE:
		err = hinic_set_led_status(nic_dev->hwdev, port,
					   HINIC_LED_TYPE_LINK,
					   HINIC_LED_MODE_FORCE_2HZ);
		if (err)
			netif_err(nic_dev, drv, netdev,
				  "Set LED blinking in 2HZ failed\n");
		break;

	case ETHTOOL_ID_INACTIVE:
		err = hinic_reset_led_status(nic_dev->hwdev, port);
		if (err)
			netif_err(nic_dev, drv, netdev,
				  "Reset LED to original status failed\n");
		break;

	default:
		return -EOPNOTSUPP;
	}

	return err;
}

static int hinic_get_module_info(struct net_device *netdev,
				 struct ethtool_modinfo *modinfo)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	u8 sfp_type_ext;
	u8 sfp_type;
	int err;

	err = hinic_get_sfp_type(nic_dev->hwdev, &sfp_type, &sfp_type_ext);
	if (err)
		return err;

	switch (sfp_type) {
	case SFF8024_ID_SFP:
		modinfo->type = ETH_MODULE_SFF_8472;
		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
		break;
	case SFF8024_ID_QSFP_8438:
		modinfo->type = ETH_MODULE_SFF_8436;
		modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
		break;
	case SFF8024_ID_QSFP_8436_8636:
		if (sfp_type_ext >= 0x3) {
			modinfo->type = ETH_MODULE_SFF_8636;
			modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;

		} else {
			modinfo->type = ETH_MODULE_SFF_8436;
			modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN;
		}
		break;
	case SFF8024_ID_QSFP28_8636:
		modinfo->type = ETH_MODULE_SFF_8636;
		modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN;
		break;
	default:
		netif_warn(nic_dev, drv, netdev,
			   "Optical module unknown: 0x%x\n", sfp_type);
		return -EINVAL;
	}

	return 0;
}

static int hinic_get_module_eeprom(struct net_device *netdev,
				   struct ethtool_eeprom *ee, u8 *data)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);
	u8 sfp_data[STD_SFP_INFO_MAX_SIZE];
	u16 len;
	int err;

	if (!ee->len || ((ee->len + ee->offset) > STD_SFP_INFO_MAX_SIZE))
		return -EINVAL;

	memset(data, 0, ee->len);

	err = hinic_get_sfp_eeprom(nic_dev->hwdev, sfp_data, &len);
	if (err)
		return err;

	memcpy(data, sfp_data + ee->offset, ee->len);

	return 0;
}

static const struct ethtool_ops hinic_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
				     ETHTOOL_COALESCE_RX_MAX_FRAMES |
				     ETHTOOL_COALESCE_TX_USECS |
				     ETHTOOL_COALESCE_TX_MAX_FRAMES,

	.get_link_ksettings = hinic_get_link_ksettings,
	.set_link_ksettings = hinic_set_link_ksettings,
	.get_drvinfo = hinic_get_drvinfo,
	.get_link = ethtool_op_get_link,
	.get_ringparam = hinic_get_ringparam,
	.set_ringparam = hinic_set_ringparam,
	.get_coalesce = hinic_get_coalesce,
	.set_coalesce = hinic_set_coalesce,
	.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
	.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
	.get_pauseparam = hinic_get_pauseparam,
	.set_pauseparam = hinic_set_pauseparam,
	.get_channels = hinic_get_channels,
	.set_channels = hinic_set_channels,
	.get_rxnfc = hinic_get_rxnfc,
	.set_rxnfc = hinic_set_rxnfc,
	.get_rxfh_key_size = hinic_get_rxfh_key_size,
	.get_rxfh_indir_size = hinic_get_rxfh_indir_size,
	.get_rxfh = hinic_get_rxfh,
	.set_rxfh = hinic_set_rxfh,
	.get_sset_count = hinic_get_sset_count,
	.get_ethtool_stats = hinic_get_ethtool_stats,
	.get_strings = hinic_get_strings,
	.self_test = hinic_diag_test,
	.set_phys_id = hinic_set_phys_id,
	.get_module_info = hinic_get_module_info,
	.get_module_eeprom = hinic_get_module_eeprom,
};

static const struct ethtool_ops hinicvf_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
				     ETHTOOL_COALESCE_RX_MAX_FRAMES |
				     ETHTOOL_COALESCE_TX_USECS |
				     ETHTOOL_COALESCE_TX_MAX_FRAMES,

	.get_link_ksettings = hinic_get_link_ksettings,
	.get_drvinfo = hinic_get_drvinfo,
	.get_link = ethtool_op_get_link,
	.get_ringparam = hinic_get_ringparam,
	.set_ringparam = hinic_set_ringparam,
	.get_coalesce = hinic_get_coalesce,
	.set_coalesce = hinic_set_coalesce,
	.get_per_queue_coalesce = hinic_get_per_queue_coalesce,
	.set_per_queue_coalesce = hinic_set_per_queue_coalesce,
	.get_channels = hinic_get_channels,
	.set_channels = hinic_set_channels,
	.get_rxnfc = hinic_get_rxnfc,
@@ -1256,5 +1849,10 @@ static const struct ethtool_ops hinic_ethtool_ops = {

void hinic_set_ethtool_ops(struct net_device *netdev)
{
	struct hinic_dev *nic_dev = netdev_priv(netdev);

	if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
		netdev->ethtool_ops = &hinic_ethtool_ops;
	else
		netdev->ethtool_ops = &hinicvf_ethtool_ops;
}
+66 −0
Original line number Diff line number Diff line
@@ -83,6 +83,8 @@ static int parse_capability(struct hinic_hwdev *hwdev,
		nic_cap->max_vf_qps = dev_cap->max_vf_sqs + 1;
	}

	hwdev->port_id = dev_cap->port_id;

	return 0;
}

@@ -705,6 +707,68 @@ static int hinic_l2nic_reset(struct hinic_hwdev *hwdev)
	return 0;
}

int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info)
{
	u16 out_size = sizeof(*interrupt_info);
	struct hinic_pfhwdev *pfhwdev;
	int err;

	if (!hwdev || !interrupt_info)
		return -EINVAL;

	pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);

	interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);

	err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
				HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
				interrupt_info, sizeof(*interrupt_info),
				interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
	if (err || !out_size || interrupt_info->status) {
		dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
			err, interrupt_info->status, out_size);
		return -EIO;
	}

	return 0;
}

int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info)
{
	u16 out_size = sizeof(*interrupt_info);
	struct hinic_msix_config temp_info;
	struct hinic_pfhwdev *pfhwdev;
	int err;

	if (!hwdev)
		return -EINVAL;

	pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);

	interrupt_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);

	err = hinic_get_interrupt_cfg(hwdev, &temp_info);
	if (err)
		return -EINVAL;

	interrupt_info->lli_credit_cnt = temp_info.lli_timer_cnt;
	interrupt_info->lli_timer_cnt = temp_info.lli_timer_cnt;

	err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
				HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
				interrupt_info, sizeof(*interrupt_info),
				interrupt_info, &out_size, HINIC_MGMT_MSG_SYNC);
	if (err || !out_size || interrupt_info->status) {
		dev_err(&hwdev->hwif->pdev->dev, "Failed to get interrupt config, err: %d, status: 0x%x, out size: 0x%x\n",
			err, interrupt_info->status, out_size);
		return -EIO;
	}

	return 0;
}

/**
 * hinic_init_hwdev - Initialize the NIC HW
 * @pdev: the NIC pci device
@@ -777,6 +841,8 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev)
		goto err_dev_cap;
	}

	mutex_init(&hwdev->func_to_io.nic_cfg.cfg_mutex);

	err = hinic_vf_func_init(hwdev);
	if (err) {
		dev_err(&pdev->dev, "Failed to init nic mbox\n");
+31 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ enum hinic_port_cmd {
	HINIC_PORT_CMD_ADD_VLAN         = 3,
	HINIC_PORT_CMD_DEL_VLAN         = 4,

	HINIC_PORT_CMD_SET_PFC		= 5,

	HINIC_PORT_CMD_SET_MAC          = 9,
	HINIC_PORT_CMD_GET_MAC          = 10,
	HINIC_PORT_CMD_DEL_MAC          = 11,
@@ -95,6 +97,9 @@ enum hinic_port_cmd {

	HINIC_PORT_CMD_FWCTXT_INIT      = 69,

	HINIC_PORT_CMD_GET_LOOPBACK_MODE = 72,
	HINIC_PORT_CMD_SET_LOOPBACK_MODE,

	HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78,

	HINIC_PORT_CMD_GET_MGMT_VERSION = 88,
@@ -125,9 +130,13 @@ enum hinic_port_cmd {

	HINIC_PORT_CMD_SET_AUTONEG	= 219,

	HINIC_PORT_CMD_GET_STD_SFP_INFO = 240,

	HINIC_PORT_CMD_SET_LRO_TIMER	= 244,

	HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 249,

	HINIC_PORT_CMD_GET_SFP_ABS	= 251,
};

/* cmd of mgmt CPU message for HILINK module */
@@ -283,6 +292,21 @@ struct hinic_cmd_l2nic_reset {
	u16	reset_flag;
};

struct hinic_msix_config {
	u8	status;
	u8	version;
	u8	rsvd0[6];

	u16	func_id;
	u16	msix_index;
	u8	pending_cnt;
	u8	coalesce_timer_cnt;
	u8	lli_timer_cnt;
	u8	lli_credit_cnt;
	u8	resend_timer_cnt;
	u8	rsvd1[3];
};

struct hinic_hwdev {
	struct hinic_hwif               *hwif;
	struct msix_entry               *msix_entries;
@@ -292,6 +316,7 @@ struct hinic_hwdev {
	struct hinic_mbox_func_to_func  *func_to_func;

	struct hinic_cap                nic_cap;
	u8				port_id;
};

struct hinic_nic_cb {
@@ -376,4 +401,10 @@ int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq,
void hinic_hwdev_set_msix_state(struct hinic_hwdev *hwdev, u16 msix_index,
				enum hinic_msix_state flag);

int hinic_get_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info);

int hinic_set_interrupt_cfg(struct hinic_hwdev *hwdev,
			    struct hinic_msix_config *interrupt_info);

#endif
+10 −0
Original line number Diff line number Diff line
@@ -47,6 +47,15 @@ struct hinic_free_db_area {
	struct semaphore        idx_lock;
};

struct hinic_nic_cfg {
	/* lock for getting nic cfg */
	struct mutex		cfg_mutex;
	bool			pause_set;
	u32			auto_neg;
	u32			rx_pause;
	u32			tx_pause;
};

struct hinic_func_to_io {
	struct hinic_hwif       *hwif;
	struct hinic_hwdev      *hwdev;
@@ -78,6 +87,7 @@ struct hinic_func_to_io {
	u16			max_vfs;
	struct vf_data_storage	*vf_infos;
	u8			link_status;
	struct hinic_nic_cfg	nic_cfg;
};

struct hinic_wq_page_size {
Loading