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

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
......@@ -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
......@@ -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)
{
netdev->ethtool_ops = &hinic_ethtool_ops;
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;
}
......@@ -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");
......
......@@ -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
......@@ -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 {
......
......@@ -78,7 +78,12 @@ enum hinic_comm_cmd {
HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP = 0x33,
HINIC_COMM_CMD_L2NIC_RESET = 0x4b,
HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP,
HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP,
HINIC_COMM_CMD_SET_LED_STATUS = 0x4a,
HINIC_COMM_CMD_L2NIC_RESET = 0x4b,
HINIC_COMM_CMD_PAGESIZE_SET = 0x50,
......
......@@ -69,6 +69,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
#define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000
#define HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT 2
#define HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG 32
#define HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG 7
static int change_mac_addr(struct net_device *netdev, const u8 *addr);
static int set_features(struct hinic_dev *nic_dev,
......@@ -887,6 +891,26 @@ static void netdev_features_init(struct net_device *netdev)
netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
}
static void hinic_refresh_nic_cfg(struct hinic_dev *nic_dev)
{
struct hinic_nic_cfg *nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg;
struct hinic_pause_config pause_info = {0};
struct hinic_port_cap port_cap = {0};
if (hinic_port_get_cap(nic_dev, &port_cap))
return;
mutex_lock(&nic_cfg->cfg_mutex);
if (nic_cfg->pause_set || !port_cap.autoneg_state) {
nic_cfg->auto_neg = port_cap.autoneg_state;
pause_info.auto_neg = nic_cfg->auto_neg;
pause_info.rx_pause = nic_cfg->rx_pause;
pause_info.tx_pause = nic_cfg->tx_pause;
hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info);
}
mutex_unlock(&nic_cfg->cfg_mutex);
}
/**
* link_status_event_handler - link event handler
* @handle: nic device for the handler
......@@ -918,6 +942,9 @@ static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
up(&nic_dev->mgmt_lock);
if (!HINIC_IS_VF(nic_dev->hwdev->hwif))
hinic_refresh_nic_cfg(nic_dev);
netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n");
} else {
down(&nic_dev->mgmt_lock);
......@@ -948,28 +975,93 @@ static int set_features(struct hinic_dev *nic_dev,
{
netdev_features_t changed = force_change ? ~0 : pre_features ^ features;
u32 csum_en = HINIC_RX_CSUM_OFFLOAD_EN;
netdev_features_t failed_features = 0;
int ret = 0;
int err = 0;
if (changed & NETIF_F_TSO)
err = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
if (changed & NETIF_F_TSO) {
ret = hinic_port_set_tso(nic_dev, (features & NETIF_F_TSO) ?
HINIC_TSO_ENABLE : HINIC_TSO_DISABLE);
if (ret) {
err = ret;
failed_features |= NETIF_F_TSO;
}
}
if (changed & NETIF_F_RXCSUM)
err = hinic_set_rx_csum_offload(nic_dev, csum_en);
if (changed & NETIF_F_RXCSUM) {
ret = hinic_set_rx_csum_offload(nic_dev, csum_en);
if (ret) {
err = ret;
failed_features |= NETIF_F_RXCSUM;
}
}
if (changed & NETIF_F_LRO) {
err = hinic_set_rx_lro_state(nic_dev,
ret = hinic_set_rx_lro_state(nic_dev,
!!(features & NETIF_F_LRO),
HINIC_LRO_RX_TIMER_DEFAULT,
HINIC_LRO_MAX_WQE_NUM_DEFAULT);
if (ret) {
err = ret;
failed_features |= NETIF_F_LRO;
}
}
if (changed & NETIF_F_HW_VLAN_CTAG_RX)
err = hinic_set_rx_vlan_offload(nic_dev,
if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
ret = hinic_set_rx_vlan_offload(nic_dev,
!!(features &
NETIF_F_HW_VLAN_CTAG_RX));
if (ret) {
err = ret;
failed_features |= NETIF_F_HW_VLAN_CTAG_RX;
}
}
return err;
if (err) {
nic_dev->netdev->features = features ^ failed_features;
return -EIO;
}
return 0;
}
static int hinic_init_intr_coalesce(struct hinic_dev *nic_dev)
{
u64 size;
u16 i;
size = sizeof(struct hinic_intr_coal_info) * nic_dev->max_qps;
nic_dev->rx_intr_coalesce = kzalloc(size, GFP_KERNEL);
if (!nic_dev->rx_intr_coalesce)
return -ENOMEM;
nic_dev->tx_intr_coalesce = kzalloc(size, GFP_KERNEL);
if (!nic_dev->tx_intr_coalesce) {
kfree(nic_dev->rx_intr_coalesce);
return -ENOMEM;
}
for (i = 0; i < nic_dev->max_qps; i++) {
nic_dev->rx_intr_coalesce[i].pending_limt =
HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT;
nic_dev->rx_intr_coalesce[i].coalesce_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG;
nic_dev->rx_intr_coalesce[i].resend_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG;
nic_dev->tx_intr_coalesce[i].pending_limt =
HINIC_DEAULT_TXRX_MSIX_PENDING_LIMIT;
nic_dev->tx_intr_coalesce[i].coalesce_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG;
nic_dev->tx_intr_coalesce[i].resend_timer_cfg =
HINIC_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG;
}
return 0;
}
static void hinic_free_intr_coalesce(struct hinic_dev *nic_dev)
{
kfree(nic_dev->tx_intr_coalesce);
kfree(nic_dev->rx_intr_coalesce);
}
/**
......@@ -1008,8 +1100,6 @@ static int nic_dev_init(struct pci_dev *pdev)
goto err_alloc_etherdev;
}
hinic_set_ethtool_ops(netdev);
if (!HINIC_IS_VF(hwdev->hwif))
netdev->netdev_ops = &hinic_netdev_ops;
else
......@@ -1032,6 +1122,8 @@ static int nic_dev_init(struct pci_dev *pdev)
nic_dev->sriov_info.pdev = pdev;
nic_dev->max_qps = num_qps;
hinic_set_ethtool_ops(netdev);
sema_init(&nic_dev->mgmt_lock, 1);
tx_stats = &nic_dev->tx_stats;
......@@ -1100,8 +1192,19 @@ static int nic_dev_init(struct pci_dev *pdev)
if (err)
goto err_set_features;
/* enable pause and disable pfc by default */
err = hinic_dcb_set_pfc(nic_dev->hwdev, 0, 0);
if (err)
goto err_set_pfc;
SET_NETDEV_DEV(netdev, &pdev->dev);
err = hinic_init_intr_coalesce(nic_dev);
if (err) {
dev_err(&pdev->dev, "Failed to init_intr_coalesce\n");
goto err_init_intr;
}
err = register_netdev(netdev);
if (err) {
dev_err(&pdev->dev, "Failed to register netdev\n");
......@@ -1111,6 +1214,9 @@ static int nic_dev_init(struct pci_dev *pdev)
return 0;
err_reg_netdev:
hinic_free_intr_coalesce(nic_dev);
err_init_intr:
err_set_pfc:
err_set_features:
hinic_hwdev_cb_unregister(nic_dev->hwdev,
HINIC_MGMT_MSG_CMD_LINK_STATUS);
......@@ -1224,6 +1330,8 @@ static void hinic_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
hinic_free_intr_coalesce(nic_dev);
hinic_port_del_mac(nic_dev, netdev->dev_addr, 0);
hinic_hwdev_cb_unregister(nic_dev->hwdev,
......
......@@ -1082,6 +1082,7 @@ int hinic_get_link_mode(struct hinic_hwdev *hwdev,
if (!hwdev || !link_mode)
return -EINVAL;
link_mode->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
out_size = sizeof(*link_mode);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_MODE,
......@@ -1172,6 +1173,8 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev,
u16 out_size = sizeof(*pause_info);
int err;
pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO,
pause_info, sizeof(*pause_info),
pause_info, &out_size);
......@@ -1190,6 +1193,8 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
u16 out_size = sizeof(*pause_info);
int err;
pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO,
pause_info, sizeof(*pause_info),
pause_info, &out_size);
......@@ -1201,3 +1206,192 @@ int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
return 0;
}
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap)
{
struct hinic_nic_cfg *nic_cfg = &hwdev->func_to_io.nic_cfg;
struct hinic_set_pfc pfc = {0};
u16 out_size = sizeof(pfc);
int err;
if (HINIC_IS_VF(hwdev->hwif))
return 0;
mutex_lock(&nic_cfg->cfg_mutex);
pfc.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif);
pfc.pfc_bitmap = pfc_bitmap;
pfc.pfc_en = pfc_en;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PFC,
&pfc, sizeof(pfc), &pfc, &out_size);
if (err || pfc.status || !out_size) {
dev_err(&hwdev->hwif->pdev->dev, "Failed to %s pfc, err: %d, status: 0x%x, out size: 0x%x\n",
pfc_en ? "enable" : "disable", err, pfc.status,
out_size);
mutex_unlock(&nic_cfg->cfg_mutex);
return -EIO;
}
/* pause settings is opposite from pfc */
nic_cfg->rx_pause = pfc_en ? 0 : 1;
nic_cfg->tx_pause = pfc_en ? 0 : 1;
mutex_unlock(&nic_cfg->cfg_mutex);
return 0;
}
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable)
{
struct hinic_port_loopback lb = {0};
u16 out_size = sizeof(lb);
int err;
lb.mode = mode;
lb.en = enable;
if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) {
dev_err(&hwdev->hwif->pdev->dev,
"Invalid loopback mode %d to set\n", mode);
return -EINVAL;
}
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE,
&lb, sizeof(lb), &lb, &out_size);
if (err || !out_size || lb.status) {
dev_err(&hwdev->hwif->pdev->dev,
"Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n",
mode, enable, err, lb.status, out_size);
return -EIO;
}
return 0;
}
static int _set_led_status(struct hinic_hwdev *hwdev, u8 port,
enum hinic_led_type type,
enum hinic_led_mode mode, u8 reset)
{
struct hinic_led_info led_info = {0};
u16 out_size = sizeof(led_info);
struct hinic_pfhwdev *pfhwdev;
int err;
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
led_info.port = port;
led_info.reset = reset;
led_info.type = type;
led_info.mode = mode;
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM,
HINIC_COMM_CMD_SET_LED_STATUS,
&led_info, sizeof(led_info),
&led_info, &out_size, HINIC_MGMT_MSG_SYNC);
if (err || led_info.status || !out_size) {
dev_err(&hwdev->hwif->pdev->dev, "Failed to set led status, err: %d, status: 0x%x, out size: 0x%x\n",
err, led_info.status, out_size);
return -EIO;
}
return 0;
}
int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port,
enum hinic_led_type type, enum hinic_led_mode mode)
{
if (!hwdev)
return -EINVAL;
return _set_led_status(hwdev, port, type, mode, 0);
}
int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port)
{
int err;
if (!hwdev)
return -EINVAL;
err = _set_led_status(hwdev, port, HINIC_LED_TYPE_INVALID,
HINIC_LED_MODE_INVALID, 1);
if (err)
dev_err(&hwdev->hwif->pdev->dev,
"Failed to reset led status\n");
return err;
}
static bool hinic_if_sfp_absent(struct hinic_hwdev *hwdev)
{
struct hinic_cmd_get_light_module_abs sfp_abs = {0};
u16 out_size = sizeof(sfp_abs);
u8 port_id = hwdev->port_id;
int err;
sfp_abs.port_id = port_id;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_SFP_ABS,
&sfp_abs, sizeof(sfp_abs), &sfp_abs,
&out_size);
if (sfp_abs.status || err || !out_size) {
dev_err(&hwdev->hwif->pdev->dev,
"Failed to get port%d sfp absent status, err: %d, status: 0x%x, out size: 0x%x\n",
port_id, err, sfp_abs.status, out_size);
return true;
}
return ((sfp_abs.abs_status == 0) ? false : true);
}
int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len)
{
struct hinic_cmd_get_std_sfp_info sfp_info = {0};
u16 out_size = sizeof(sfp_info);
u8 port_id;
int err;
if (!hwdev || !data || !len)
return -EINVAL;
port_id = hwdev->port_id;
if (hinic_if_sfp_absent(hwdev))
return -ENXIO;
sfp_info.port_id = port_id;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_STD_SFP_INFO,
&sfp_info, sizeof(sfp_info), &sfp_info,
&out_size);
if (sfp_info.status || err || !out_size) {
dev_err(&hwdev->hwif->pdev->dev,
"Failed to get port%d sfp eeprom information, err: %d, status: 0x%x, out size: 0x%x\n",
port_id, err, sfp_info.status, out_size);
return -EIO;
}
*len = min_t(u16, sfp_info.eeprom_len, STD_SFP_INFO_MAX_SIZE);
memcpy(data, sfp_info.sfp_info, STD_SFP_INFO_MAX_SIZE);
return 0;
}
int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1)
{
u8 sfp_data[STD_SFP_INFO_MAX_SIZE];
u16 len;
int err;
if (hinic_if_sfp_absent(hwdev))
return -ENXIO;
err = hinic_get_sfp_eeprom(hwdev, sfp_data, &len);
if (err)
return err;
*data0 = sfp_data[0];
*data1 = sfp_data[1];
return 0;
}
......@@ -641,6 +641,68 @@ struct hinic_pause_config {
u32 tx_pause;
};
struct hinic_set_pfc {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_id;
u8 pfc_en;
u8 pfc_bitmap;
u8 rsvd1[4];
};
/* get or set loopback mode, need to modify by base API */
#define HINIC_INTERNAL_LP_MODE 5
#define LOOP_MODE_MIN 1
#define LOOP_MODE_MAX 6
struct hinic_port_loopback {
u8 status;
u8 version;
u8 rsvd[6];
u32 mode;
u32 en;
};
struct hinic_led_info {
u8 status;
u8 version;
u8 rsvd0[6];
u8 port;
u8 type;
u8 mode;
u8 reset;
};
#define STD_SFP_INFO_MAX_SIZE 640
struct hinic_cmd_get_light_module_abs {
u8 status;
u8 version;
u8 rsvd0[6];
u8 port_id;
u8 abs_status; /* 0:present, 1:absent */
u8 rsv[2];
};
#define STD_SFP_INFO_MAX_SIZE 640
struct hinic_cmd_get_std_sfp_info {
u8 status;
u8 version;
u8 rsvd0[6];
u8 port_id;
u8 wire_type;
u16 eeprom_len;
u32 rsvd;
u8 sfp_info[STD_SFP_INFO_MAX_SIZE];
};
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id);
......@@ -736,6 +798,38 @@ int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev,
int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev,
struct hinic_pause_config *pause_info);
int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap);
int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable);
enum hinic_led_mode {
HINIC_LED_MODE_ON,
HINIC_LED_MODE_OFF,
HINIC_LED_MODE_FORCE_1HZ,
HINIC_LED_MODE_FORCE_2HZ,
HINIC_LED_MODE_FORCE_4HZ,
HINIC_LED_MODE_1HZ,
HINIC_LED_MODE_2HZ,
HINIC_LED_MODE_4HZ,
HINIC_LED_MODE_INVALID,
};
enum hinic_led_type {
HINIC_LED_TYPE_LINK,
HINIC_LED_TYPE_LOW_SPEED,
HINIC_LED_TYPE_HIGH_SPEED,
HINIC_LED_TYPE_INVALID,
};
int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port);
int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port,
enum hinic_led_type type, enum hinic_led_mode mode);
int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1);
int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len);
int hinic_open(struct net_device *netdev);
int hinic_close(struct net_device *netdev);
......
......@@ -316,6 +316,39 @@ static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb,
return num_wqes;
}
static void hinic_copy_lp_data(struct hinic_dev *nic_dev,
struct sk_buff *skb)
{
struct net_device *netdev = nic_dev->netdev;
u8 *lb_buf = nic_dev->lb_test_rx_buf;
int lb_len = nic_dev->lb_pkt_len;
int pkt_offset, frag_len, i;
void *frag_data = NULL;
if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) {
nic_dev->lb_test_rx_idx = 0;
netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n");
}
if (skb->len != nic_dev->lb_pkt_len) {
netif_warn(nic_dev, drv, netdev, "Wrong packet length\n");
nic_dev->lb_test_rx_idx++;
return;
}
pkt_offset = nic_dev->lb_test_rx_idx * lb_len;
frag_len = (int)skb_headlen(skb);
memcpy(lb_buf + pkt_offset, skb->data, frag_len);
pkt_offset += frag_len;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]);
frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]);
memcpy((lb_buf + pkt_offset), frag_data, frag_len);
pkt_offset += frag_len;
}
nic_dev->lb_test_rx_idx++;
}
/**
* rxq_recv - Rx handler
* @rxq: rx queue
......@@ -330,6 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
u64 pkt_len = 0, rx_bytes = 0;
struct hinic_rq *rq = rxq->rq;
struct hinic_rq_wqe *rq_wqe;
struct hinic_dev *nic_dev;
unsigned int free_wqebbs;
struct hinic_rq_cqe *cqe;
int num_wqes, pkts = 0;
......@@ -342,6 +376,8 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
u32 vlan_len;
u16 vid;
nic_dev = netdev_priv(netdev);
while (pkts < budget) {
num_wqes = 0;
......@@ -384,6 +420,9 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
}
if (unlikely(nic_dev->flags & HINIC_LP_TEST))
hinic_copy_lp_data(nic_dev, skb);
skb_record_rx_queue(skb, qp->q_id);
skb->protocol = eth_type_trans(skb, rxq->netdev);
......@@ -478,11 +517,15 @@ static irqreturn_t rx_irq(int irq, void *data)
static int rx_request_irq(struct hinic_rxq *rxq)
{
struct hinic_dev *nic_dev = netdev_priv(rxq->netdev);
struct hinic_msix_config interrupt_info = {0};
struct hinic_intr_coal_info *intr_coal = NULL;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_rq *rq = rxq->rq;
struct hinic_qp *qp;
int err;
qp = container_of(rq, struct hinic_qp, rq);
rx_add_napi(rxq);
hinic_hwdev_msix_set(hwdev, rq->msix_entry,
......@@ -490,13 +533,26 @@ static int rx_request_irq(struct hinic_rxq *rxq)
RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT,
RX_IRQ_NO_RESEND_TIMER);
intr_coal = &nic_dev->rx_intr_coalesce[qp->q_id];
interrupt_info.msix_index = rq->msix_entry;
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(hwdev, &interrupt_info);
if (err) {
netif_err(nic_dev, drv, rxq->netdev,
"Failed to set RX interrupt coalescing attribute\n");
rx_del_napi(rxq);
return err;
}
err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq);
if (err) {
rx_del_napi(rxq);
return err;
}
qp = container_of(rq, struct hinic_qp, rq);
cpumask_set_cpu(qp->q_id % num_online_cpus(), &rq->affinity_mask);
return irq_set_affinity_hint(rq->irq, &rq->affinity_mask);
}
......
......@@ -383,7 +383,7 @@ static int hinic_del_vf_mac_msg_handler(void *hwdev, u16 vf_id,
nic_io = &hw_dev->func_to_io;
vf_info = nic_io->vf_infos + HW_VF_ID_TO_OS(vf_id);
if (vf_info->pf_set_mac && is_valid_ether_addr(mac_in->mac) &&
if (vf_info->pf_set_mac && is_valid_ether_addr(mac_in->mac) &&
!memcmp(vf_info->vf_mac_addr, mac_in->mac, ETH_ALEN)) {
dev_warn(&hw_dev->hwif->pdev->dev, "PF has already set VF mac.\n");
mac_out->status = HINIC_PF_SET_VF_ALREADY;
......@@ -905,7 +905,6 @@ int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting)
err = hinic_set_vf_spoofchk(sriov_info->hwdev,
OS_VF_ID_TO_HW(vf), setting);
if (!err) {
netif_info(nic_dev, drv, netdev, "Set VF %d spoofchk %s successfully\n",
vf, setting ? "on" : "off");
......@@ -1020,6 +1019,7 @@ static int cfg_mbx_pf_proc_vf_msg(void *hwdev, u16 vf_id, u8 cmd, void *buf_in,
dev_cap->max_vf = cap->max_vf;
dev_cap->max_sqs = cap->max_vf_qps;
dev_cap->max_rqs = cap->max_vf_qps;
dev_cap->port_id = dev->port_id;
*out_size = sizeof(*dev_cap);
......
......@@ -459,6 +459,67 @@ static int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task,
return 0;
}
netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
u16 prod_idx, q_id = skb->queue_mapping;
struct netdev_queue *netdev_txq;
int nr_sges, err = NETDEV_TX_OK;
struct hinic_sq_wqe *sq_wqe;
unsigned int wqe_size;
struct hinic_txq *txq;
struct hinic_qp *qp;
txq = &nic_dev->txqs[q_id];
qp = container_of(txq->sq, struct hinic_qp, sq);
nr_sges = skb_shinfo(skb)->nr_frags + 1;
err = tx_map_skb(nic_dev, skb, txq->sges);
if (err)
goto skb_error;
wqe_size = HINIC_SQ_WQE_SIZE(nr_sges);
sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx);
if (!sq_wqe) {
netif_stop_subqueue(netdev, qp->q_id);
sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx);
if (sq_wqe) {
netif_wake_subqueue(nic_dev->netdev, qp->q_id);
goto process_sq_wqe;
}
tx_unmap_skb(nic_dev, skb, txq->sges);
u64_stats_update_begin(&txq->txq_stats.syncp);
txq->txq_stats.tx_busy++;
u64_stats_update_end(&txq->txq_stats.syncp);
err = NETDEV_TX_BUSY;
wqe_size = 0;
goto flush_skbs;
}
process_sq_wqe:
hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges);
hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size);
flush_skbs:
netdev_txq = netdev_get_tx_queue(netdev, q_id);
if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq)))
hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0);
return err;
skb_error:
dev_kfree_skb_any(skb);
u64_stats_update_begin(&txq->txq_stats.syncp);
txq->txq_stats.tx_dropped++;
u64_stats_update_end(&txq->txq_stats.syncp);
return NETDEV_TX_OK;
}
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
......@@ -718,12 +779,17 @@ static irqreturn_t tx_irq(int irq, void *data)
static int tx_request_irq(struct hinic_txq *txq)
{
struct hinic_dev *nic_dev = netdev_priv(txq->netdev);
struct hinic_msix_config interrupt_info = {0};
struct hinic_intr_coal_info *intr_coal = NULL;
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_sq *sq = txq->sq;
struct hinic_qp *qp;
int err;
qp = container_of(sq, struct hinic_qp, sq);
tx_napi_add(txq, nic_dev->tx_weight);
hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry,
......@@ -731,6 +797,20 @@ static int tx_request_irq(struct hinic_txq *txq)
TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT,
TX_IRQ_NO_RESEND_TIMER);
intr_coal = &nic_dev->tx_intr_coalesce[qp->q_id];
interrupt_info.msix_index = sq->msix_entry;
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(hwdev, &interrupt_info);
if (err) {
netif_err(nic_dev, drv, txq->netdev,
"Failed to set TX interrupt coalescing attribute\n");
tx_napi_del(txq);
return err;
}
err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq);
if (err) {
dev_err(&pdev->dev, "Failed to request Tx irq\n");
......
......@@ -44,6 +44,8 @@ void hinic_txq_clean_stats(struct hinic_txq *txq);
void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats);
netdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment