diff options
Diffstat (limited to 'drivers/net/ethernet/huawei')
17 files changed, 1239 insertions, 48 deletions
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index a621ebbf7610..48b40be3e84d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -69,6 +69,8 @@ struct hinic_dev { struct hinic_txq *txqs; struct hinic_rxq *rxqs; + u16 sq_depth; + u16 rq_depth; struct hinic_txq_stats tx_stats; struct hinic_rxq_stats rx_stats; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index 966aea949c0b..ace18d258049 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -33,6 +33,99 @@ #include "hinic_rx.h" #include "hinic_dev.h" +#define SET_LINK_STR_MAX_LEN 128 + +#define GET_SUPPORTED_MODE 0 +#define GET_ADVERTISED_MODE 1 + +#define ETHTOOL_ADD_SUPPORTED_SPEED_LINK_MODE(ecmd, mode) \ + ((ecmd)->supported |= \ + (1UL << hw_to_ethtool_link_mode_table[mode].link_mode_bit)) +#define ETHTOOL_ADD_ADVERTISED_SPEED_LINK_MODE(ecmd, mode) \ + ((ecmd)->advertising |= \ + (1UL << hw_to_ethtool_link_mode_table[mode].link_mode_bit)) +#define ETHTOOL_ADD_SUPPORTED_LINK_MODE(ecmd, mode) \ + ((ecmd)->supported |= SUPPORTED_##mode) +#define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \ + ((ecmd)->advertising |= ADVERTISED_##mode) + +struct hw2ethtool_link_mode { + enum ethtool_link_mode_bit_indices link_mode_bit; + u32 speed; + enum hinic_link_mode hw_link_mode; +}; + +struct cmd_link_settings { + u64 supported; + u64 advertising; + + u32 speed; + u8 duplex; + u8 port; + u8 autoneg; +}; + +static u32 hw_to_ethtool_speed[LINK_SPEED_LEVELS] = { + SPEED_10, SPEED_100, + SPEED_1000, SPEED_10000, + SPEED_25000, SPEED_40000, + SPEED_100000 +}; + +static struct hw2ethtool_link_mode + hw_to_ethtool_link_mode_table[HINIC_LINK_MODE_NUMBERS] = { + { + .link_mode_bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + .speed = SPEED_10000, + .hw_link_mode = HINIC_10GE_BASE_KR, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, + .speed = SPEED_40000, + .hw_link_mode = HINIC_40GE_BASE_KR4, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, + .speed = SPEED_40000, + .hw_link_mode = HINIC_40GE_BASE_CR4, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, + .speed = SPEED_100000, + .hw_link_mode = HINIC_100GE_BASE_KR4, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, + .speed = SPEED_100000, + .hw_link_mode = HINIC_100GE_BASE_CR4, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, + .speed = SPEED_25000, + .hw_link_mode = HINIC_25GE_BASE_KR_S, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, + .speed = SPEED_25000, + .hw_link_mode = HINIC_25GE_BASE_CR_S, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, + .speed = SPEED_25000, + .hw_link_mode = HINIC_25GE_BASE_KR, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, + .speed = SPEED_25000, + .hw_link_mode = HINIC_25GE_BASE_CR, + }, + { + .link_mode_bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + .speed = SPEED_1000, + .hw_link_mode = HINIC_GE_BASE_KX, + }, +}; + static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, enum hinic_speed speed) { @@ -71,18 +164,91 @@ static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, } } +static int hinic_get_link_mode_index(enum hinic_link_mode link_mode) +{ + int i = 0; + + for (i = 0; i < HINIC_LINK_MODE_NUMBERS; i++) { + if (link_mode == hw_to_ethtool_link_mode_table[i].hw_link_mode) + break; + } + + return i; +} + +static void hinic_add_ethtool_link_mode(struct cmd_link_settings *link_settings, + enum hinic_link_mode hw_link_mode, + u32 name) +{ + enum hinic_link_mode link_mode; + int idx = 0; + + for (link_mode = 0; link_mode < HINIC_LINK_MODE_NUMBERS; link_mode++) { + if (hw_link_mode & ((u32)1 << link_mode)) { + idx = hinic_get_link_mode_index(link_mode); + if (idx >= HINIC_LINK_MODE_NUMBERS) + continue; + + if (name == GET_SUPPORTED_MODE) + ETHTOOL_ADD_SUPPORTED_SPEED_LINK_MODE + (link_settings, idx); + else + ETHTOOL_ADD_ADVERTISED_SPEED_LINK_MODE + (link_settings, idx); + } + } +} + +static void hinic_link_port_type(struct cmd_link_settings *link_settings, + enum hinic_port_type port_type) +{ + switch (port_type) { + case HINIC_PORT_ELEC: + case HINIC_PORT_TP: + ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, TP); + ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, TP); + link_settings->port = PORT_TP; + break; + + case HINIC_PORT_AOC: + case HINIC_PORT_FIBRE: + ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, FIBRE); + ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, FIBRE); + link_settings->port = PORT_FIBRE; + break; + + case HINIC_PORT_COPPER: + ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, FIBRE); + ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, FIBRE); + link_settings->port = PORT_DA; + break; + + case HINIC_PORT_BACKPLANE: + ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, Backplane); + ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Backplane); + link_settings->port = PORT_NONE; + break; + + default: + link_settings->port = PORT_OTHER; + break; + } +} + static int hinic_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *link_ksettings) { struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_link_mode_cmd link_mode = { 0 }; + struct hinic_pause_config pause_info = { 0 }; + struct cmd_link_settings settings = { 0 }; enum hinic_port_link_state link_state; struct hinic_port_cap port_cap; int err; + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); - ethtool_link_ksettings_add_link_mode(link_ksettings, supported, - Autoneg); link_ksettings->base.speed = SPEED_UNKNOWN; link_ksettings->base.autoneg = AUTONEG_DISABLE; @@ -92,14 +258,19 @@ static int hinic_get_link_ksettings(struct net_device *netdev, if (err) return err; + hinic_link_port_type(&settings, port_cap.port_type); + link_ksettings->base.port = settings.port; + err = hinic_port_link_state(nic_dev, &link_state); if (err) return err; - if (link_state != HINIC_LINK_STATE_UP) - return err; - - set_link_speed(link_ksettings, port_cap.speed); + if (link_state == HINIC_LINK_STATE_UP) { + set_link_speed(link_ksettings, port_cap.speed); + link_ksettings->base.duplex = + (port_cap.duplex == HINIC_DUPLEX_FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + } if (!!(port_cap.autoneg_cap & HINIC_AUTONEG_SUPPORTED)) ethtool_link_ksettings_add_link_mode(link_ksettings, @@ -108,11 +279,243 @@ static int hinic_get_link_ksettings(struct net_device *netdev, if (port_cap.autoneg_state == HINIC_AUTONEG_ACTIVE) link_ksettings->base.autoneg = AUTONEG_ENABLE; - link_ksettings->base.duplex = (port_cap.duplex == HINIC_DUPLEX_FULL) ? - DUPLEX_FULL : DUPLEX_HALF; + err = hinic_get_link_mode(nic_dev->hwdev, &link_mode); + if (err || link_mode.supported == HINIC_SUPPORTED_UNKNOWN || + link_mode.advertised == HINIC_SUPPORTED_UNKNOWN) + return -EIO; + + hinic_add_ethtool_link_mode(&settings, link_mode.supported, + GET_SUPPORTED_MODE); + hinic_add_ethtool_link_mode(&settings, link_mode.advertised, + GET_ADVERTISED_MODE); + + if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) { + err = hinic_get_hw_pause_info(nic_dev->hwdev, &pause_info); + if (err) + return err; + ETHTOOL_ADD_SUPPORTED_LINK_MODE(&settings, Pause); + if (pause_info.rx_pause && pause_info.tx_pause) { + ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Pause); + } else if (pause_info.tx_pause) { + ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Asym_Pause); + } else if (pause_info.rx_pause) { + ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Pause); + ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Asym_Pause); + } + } + + bitmap_copy(link_ksettings->link_modes.supported, + (unsigned long *)&settings.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_copy(link_ksettings->link_modes.advertising, + (unsigned long *)&settings.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + return 0; +} + +static int hinic_ethtool_to_hw_speed_level(u32 speed) +{ + int i; + + for (i = 0; i < LINK_SPEED_LEVELS; i++) { + if (hw_to_ethtool_speed[i] == speed) + break; + } + + return i; +} + +static bool hinic_is_support_speed(enum hinic_link_mode supported_link, + u32 speed) +{ + enum hinic_link_mode link_mode; + int idx; + + for (link_mode = 0; link_mode < HINIC_LINK_MODE_NUMBERS; link_mode++) { + if (!(supported_link & ((u32)1 << link_mode))) + continue; + + idx = hinic_get_link_mode_index(link_mode); + if (idx >= HINIC_LINK_MODE_NUMBERS) + continue; + + if (hw_to_ethtool_link_mode_table[idx].speed == speed) + return true; + } + + return false; +} + +static bool hinic_is_speed_legal(struct hinic_dev *nic_dev, u32 speed) +{ + struct hinic_link_mode_cmd link_mode = { 0 }; + struct net_device *netdev = nic_dev->netdev; + enum nic_speed_level speed_level = 0; + int err; + + err = hinic_get_link_mode(nic_dev->hwdev, &link_mode); + if (err) + return false; + + if (link_mode.supported == HINIC_SUPPORTED_UNKNOWN || + link_mode.advertised == HINIC_SUPPORTED_UNKNOWN) + return false; + + speed_level = hinic_ethtool_to_hw_speed_level(speed); + if (speed_level >= LINK_SPEED_LEVELS || + !hinic_is_support_speed(link_mode.supported, speed)) { + netif_err(nic_dev, drv, netdev, + "Unsupported speed: %d\n", speed); + return false; + } + + return true; +} + +static int get_link_settings_type(struct hinic_dev *nic_dev, + u8 autoneg, u32 speed, u32 *set_settings) +{ + struct hinic_port_cap port_cap = { 0 }; + int err; + + err = hinic_port_get_cap(nic_dev, &port_cap); + if (err) + return err; + + /* always set autonegotiation */ + if (port_cap.autoneg_cap) + *set_settings |= HILINK_LINK_SET_AUTONEG; + + if (autoneg == AUTONEG_ENABLE) { + if (!port_cap.autoneg_cap) { + netif_err(nic_dev, drv, nic_dev->netdev, "Not support autoneg\n"); + return -EOPNOTSUPP; + } + } else if (speed != (u32)SPEED_UNKNOWN) { + /* set speed only when autoneg is disabled */ + if (!hinic_is_speed_legal(nic_dev, speed)) + return -EINVAL; + *set_settings |= HILINK_LINK_SET_SPEED; + } else { + netif_err(nic_dev, drv, nic_dev->netdev, "Need to set speed when autoneg is off\n"); + return -EOPNOTSUPP; + } + return 0; } +static int set_link_settings_separate_cmd(struct hinic_dev *nic_dev, + u32 set_settings, u8 autoneg, + u32 speed) +{ + enum nic_speed_level speed_level = 0; + int err = 0; + + if (set_settings & HILINK_LINK_SET_AUTONEG) { + err = hinic_set_autoneg(nic_dev->hwdev, + (autoneg == AUTONEG_ENABLE)); + if (err) + netif_err(nic_dev, drv, nic_dev->netdev, "%s autoneg failed\n", + (autoneg == AUTONEG_ENABLE) ? + "Enable" : "Disable"); + else + netif_info(nic_dev, drv, nic_dev->netdev, "%s autoneg successfully\n", + (autoneg == AUTONEG_ENABLE) ? + "Enable" : "Disable"); + } + + if (!err && (set_settings & HILINK_LINK_SET_SPEED)) { + speed_level = hinic_ethtool_to_hw_speed_level(speed); + err = hinic_set_speed(nic_dev->hwdev, speed_level); + if (err) + netif_err(nic_dev, drv, nic_dev->netdev, "Set speed %d failed\n", + speed); + else + netif_info(nic_dev, drv, nic_dev->netdev, "Set speed %d successfully\n", + speed); + } + + return err; +} + +static int hinic_set_settings_to_hw(struct hinic_dev *nic_dev, + u32 set_settings, u8 autoneg, u32 speed) +{ + struct hinic_link_ksettings_info settings = {0}; + char set_link_str[SET_LINK_STR_MAX_LEN] = {0}; + struct net_device *netdev = nic_dev->netdev; + enum nic_speed_level speed_level = 0; + int err; + + err = snprintf(set_link_str, SET_LINK_STR_MAX_LEN, "%s", + (set_settings & HILINK_LINK_SET_AUTONEG) ? + (autoneg ? "autong enable " : "autong disable ") : ""); + if (err < 0 || err >= SET_LINK_STR_MAX_LEN) { + netif_err(nic_dev, drv, netdev, "Failed to snprintf link state, function return(%d) and dest_len(%d)\n", + err, SET_LINK_STR_MAX_LEN); + return -EFAULT; + } + + if (set_settings & HILINK_LINK_SET_SPEED) { + speed_level = hinic_ethtool_to_hw_speed_level(speed); + err = snprintf(set_link_str, SET_LINK_STR_MAX_LEN, + "%sspeed %d ", set_link_str, speed); + if (err <= 0 || err >= SET_LINK_STR_MAX_LEN) { + netif_err(nic_dev, drv, netdev, "Failed to snprintf link speed, function return(%d) and dest_len(%d)\n", + err, SET_LINK_STR_MAX_LEN); + return -EFAULT; + } + } + + settings.func_id = HINIC_HWIF_FUNC_IDX(nic_dev->hwdev->hwif); + settings.valid_bitmap = set_settings; + settings.autoneg = autoneg; + settings.speed = speed_level; + + err = hinic_set_link_settings(nic_dev->hwdev, &settings); + if (err != HINIC_MGMT_CMD_UNSUPPORTED) { + if (err) + netif_err(nic_dev, drv, netdev, "Set %s failed\n", + set_link_str); + else + netif_info(nic_dev, drv, netdev, "Set %s successfully\n", + set_link_str); + + return err; + } + + return set_link_settings_separate_cmd(nic_dev, set_settings, autoneg, + speed); +} + +static int set_link_settings(struct net_device *netdev, u8 autoneg, u32 speed) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u32 set_settings = 0; + int err; + + err = get_link_settings_type(nic_dev, autoneg, speed, &set_settings); + if (err) + return err; + + if (set_settings) + err = hinic_set_settings_to_hw(nic_dev, set_settings, + autoneg, speed); + else + netif_info(nic_dev, drv, netdev, "Nothing changed, exit without setting anything\n"); + + return err; +} + +static int hinic_set_link_ksettings(struct net_device *netdev, const struct + ethtool_link_ksettings *link_settings) +{ + /* only support to set autoneg and speed */ + return set_link_settings(netdev, link_settings->base.autoneg, + link_settings->base.speed); +} + static void hinic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { @@ -135,12 +538,81 @@ static void hinic_get_drvinfo(struct net_device *netdev, static void hinic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { - ring->rx_max_pending = HINIC_RQ_DEPTH; - ring->tx_max_pending = HINIC_SQ_DEPTH; - ring->rx_pending = HINIC_RQ_DEPTH; - ring->tx_pending = HINIC_SQ_DEPTH; + struct hinic_dev *nic_dev = netdev_priv(netdev); + + ring->rx_max_pending = HINIC_MAX_QUEUE_DEPTH; + ring->tx_max_pending = HINIC_MAX_QUEUE_DEPTH; + ring->rx_pending = nic_dev->rq_depth; + ring->tx_pending = nic_dev->sq_depth; } +static int check_ringparam_valid(struct hinic_dev *nic_dev, + struct ethtool_ringparam *ring) +{ + if (ring->rx_jumbo_pending || ring->rx_mini_pending) { + netif_err(nic_dev, drv, nic_dev->netdev, + "Unsupported rx_jumbo_pending/rx_mini_pending\n"); + return -EINVAL; + } + + if (ring->tx_pending > HINIC_MAX_QUEUE_DEPTH || + ring->tx_pending < HINIC_MIN_QUEUE_DEPTH || + ring->rx_pending > HINIC_MAX_QUEUE_DEPTH || + ring->rx_pending < HINIC_MIN_QUEUE_DEPTH) { + netif_err(nic_dev, drv, nic_dev->netdev, + "Queue depth out of range [%d-%d]\n", + HINIC_MIN_QUEUE_DEPTH, HINIC_MAX_QUEUE_DEPTH); + return -EINVAL; + } + + return 0; +} + +static int hinic_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 new_sq_depth, new_rq_depth; + int err; + + err = check_ringparam_valid(nic_dev, ring); + if (err) + return err; + + new_sq_depth = (u16)(1U << (u16)ilog2(ring->tx_pending)); + new_rq_depth = (u16)(1U << (u16)ilog2(ring->rx_pending)); + + if (new_sq_depth == nic_dev->sq_depth && + new_rq_depth == nic_dev->rq_depth) + return 0; + + netif_info(nic_dev, drv, netdev, + "Change Tx/Rx ring depth from %d/%d to %d/%d\n", + nic_dev->sq_depth, nic_dev->rq_depth, + new_sq_depth, new_rq_depth); + + nic_dev->sq_depth = new_sq_depth; + nic_dev->rq_depth = new_rq_depth; + + if (netif_running(netdev)) { + netif_info(nic_dev, drv, netdev, "Restarting netdev\n"); + err = hinic_close(netdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to close netdev\n"); + return -EFAULT; + } + + err = hinic_open(netdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to open netdev\n"); + return -EFAULT; + } + } + + return 0; +} static void hinic_get_channels(struct net_device *netdev, struct ethtool_channels *channels) { @@ -741,9 +1213,11 @@ static void hinic_get_strings(struct net_device *netdev, static const struct ethtool_ops hinic_ethtool_ops = { .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_channels = hinic_get_channels, .get_rxnfc = hinic_get_rxnfc, .set_rxnfc = hinic_set_rxnfc, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c index 33c5333657c1..cb5b6e5f787f 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -849,6 +849,25 @@ err_init_cmdq: return err; } +static int hinic_set_cmdq_depth(struct hinic_hwdev *hwdev, u16 cmdq_depth) +{ + struct hinic_cmd_hw_ioctxt hw_ioctxt = { 0 }; + struct hinic_pfhwdev *pfhwdev; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + hw_ioctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + hw_ioctxt.ppf_idx = HINIC_HWIF_PPF_IDX(hwdev->hwif); + + hw_ioctxt.set_cmdq_depth = HW_IOCTXT_SET_CMDQ_DEPTH_ENABLE; + hw_ioctxt.cmdq_depth = (u8)ilog2(cmdq_depth); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_HWCTXT_SET, + &hw_ioctxt, sizeof(hw_ioctxt), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} + /** * hinic_init_cmdqs - init all cmdqs * @cmdqs: cmdqs to init @@ -899,8 +918,18 @@ int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, hinic_ceq_register_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ, cmdqs, cmdq_ceq_handler); + + err = hinic_set_cmdq_depth(hwdev, CMDQ_DEPTH); + if (err) { + dev_err(&hwif->pdev->dev, "Failed to set cmdq depth\n"); + goto err_set_cmdq_depth; + } + return 0; +err_set_cmdq_depth: + hinic_ceq_unregister_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ); + err_cmdq_ctxt: hinic_wqs_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, HINIC_MAX_CMDQ_TYPES); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index e5cab58e4ddd..0245da02efbb 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -44,10 +44,6 @@ enum io_status { IO_RUNNING = 1, }; -enum hw_ioctxt_set_cmdq_depth { - HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT, -}; - /** * get_capability - convert device capabilities to NIC capabilities * @hwdev: the HW device to set and convert device capabilities for @@ -223,6 +219,19 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, HINIC_MGMT_MSG_SYNC); } +int hinic_hilink_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_hilink_cmd cmd, + void *buf_in, u16 in_size, void *buf_out, + u16 *out_size) +{ + struct hinic_pfhwdev *pfhwdev; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_HILINK, cmd, + buf_in, in_size, buf_out, out_size, + HINIC_MGMT_MSG_SYNC); +} + /** * init_fw_ctxt- Init Firmware tables before network mgmt and io operations * @hwdev: the NIC HW device @@ -260,8 +269,8 @@ static int init_fw_ctxt(struct hinic_hwdev *hwdev) * * Return 0 - Success, negative - Failure **/ -static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth, - unsigned int sq_depth) +static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int sq_depth, + unsigned int rq_depth) { struct hinic_hwif *hwif = hwdev->hwif; struct hinic_cmd_hw_ioctxt hw_ioctxt; @@ -426,7 +435,7 @@ static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn) * * Return 0 - Success, negative - Failure **/ -int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) +int hinic_hwdev_ifup(struct hinic_hwdev *hwdev, u16 sq_depth, u16 rq_depth) { struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; struct hinic_cap *nic_cap = &hwdev->nic_cap; @@ -449,6 +458,9 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) ceq_msix_entries = &hwdev->msix_entries[num_aeqs]; func_to_io->hwdev = hwdev; + func_to_io->sq_depth = sq_depth; + func_to_io->rq_depth = rq_depth; + err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, num_ceqs, ceq_msix_entries); if (err) { @@ -473,7 +485,7 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) hinic_db_state_set(hwif, HINIC_DB_ENABLE); } - err = set_hw_ioctxt(hwdev, HINIC_SQ_DEPTH, HINIC_RQ_DEPTH); + err = set_hw_ioctxt(hwdev, sq_depth, rq_depth); if (err) { dev_err(&pdev->dev, "Failed to set HW IO ctxt\n"); goto err_hw_ioctxt; @@ -667,6 +679,32 @@ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); } +static int hinic_l2nic_reset(struct hinic_hwdev *hwdev) +{ + struct hinic_cmd_l2nic_reset l2nic_reset = {0}; + u16 out_size = sizeof(l2nic_reset); + struct hinic_pfhwdev *pfhwdev; + int err; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + l2nic_reset.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + /* 0 represents standard l2nic reset flow */ + l2nic_reset.reset_flag = 0; + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_L2NIC_RESET, &l2nic_reset, + sizeof(l2nic_reset), &l2nic_reset, + &out_size, HINIC_MGMT_MSG_SYNC); + if (err || !out_size || l2nic_reset.status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to reset L2NIC resources, err: %d, status: 0x%x, out_size: 0x%x\n", + err, l2nic_reset.status, out_size); + return -EIO; + } + + return 0; +} + /** * hinic_init_hwdev - Initialize the NIC HW * @pdev: the NIC pci device @@ -729,6 +767,10 @@ struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) goto err_init_pfhwdev; } + err = hinic_l2nic_reset(hwdev); + if (err) + goto err_l2nic_reset; + err = get_dev_cap(hwdev); if (err) { dev_err(&pdev->dev, "Failed to get device capabilities\n"); @@ -759,6 +801,7 @@ err_resources_state: err_init_fw_ctxt: hinic_vf_func_free(hwdev); err_vf_func_init: +err_l2nic_reset: err_dev_cap: free_pfhwdev(pfhwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 531d1072e0df..71ea7e46dbbc 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -25,6 +25,7 @@ #define HINIC_PF_SET_VF_ALREADY 0x4 #define HINIC_MGMT_STATUS_EXIST 0x6 +#define HINIC_MGMT_CMD_UNSUPPORTED 0xFF struct hinic_cap { u16 max_qps; @@ -33,6 +34,11 @@ struct hinic_cap { u16 max_vf_qps; }; +enum hw_ioctxt_set_cmdq_depth { + HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT, + HW_IOCTXT_SET_CMDQ_DEPTH_ENABLE, +}; + enum hinic_port_cmd { HINIC_PORT_CMD_VF_REGISTER = 0x0, HINIC_PORT_CMD_VF_UNREGISTER = 0x1, @@ -48,6 +54,9 @@ enum hinic_port_cmd { HINIC_PORT_CMD_SET_RX_MODE = 12, + HINIC_PORT_CMD_GET_PAUSE_INFO = 20, + HINIC_PORT_CMD_SET_PAUSE_INFO = 21, + HINIC_PORT_CMD_GET_LINK_STATE = 24, HINIC_PORT_CMD_SET_LRO = 25, @@ -86,12 +95,16 @@ enum hinic_port_cmd { HINIC_PORT_CMD_FWCTXT_INIT = 69, + HINIC_PORT_CMD_ENABLE_SPOOFCHK = 78, + HINIC_PORT_CMD_GET_MGMT_VERSION = 88, HINIC_PORT_CMD_SET_FUNC_STATE = 93, HINIC_PORT_CMD_GET_GLOBAL_QPN = 102, + HINIC_PORT_CMD_SET_VF_RATE = 105, + HINIC_PORT_CMD_SET_VF_VLAN = 106, HINIC_PORT_CMD_CLR_VF_VLAN, @@ -106,7 +119,21 @@ enum hinic_port_cmd { HINIC_PORT_CMD_GET_CAP = 170, + HINIC_PORT_CMD_GET_LINK_MODE = 217, + + HINIC_PORT_CMD_SET_SPEED = 218, + + HINIC_PORT_CMD_SET_AUTONEG = 219, + HINIC_PORT_CMD_SET_LRO_TIMER = 244, + + HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE = 249, +}; + +/* cmd of mgmt CPU message for HILINK module */ +enum hinic_hilink_cmd { + HINIC_HILINK_CMD_GET_LINK_INFO = 0x3, + HINIC_HILINK_CMD_SET_LINK_SETTINGS = 0x8, }; enum hinic_ucode_cmd { @@ -247,6 +274,15 @@ struct hinic_cmd_hw_ci { u64 ci_addr; }; +struct hinic_cmd_l2nic_reset { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 reset_flag; +}; + struct hinic_hwdev { struct hinic_hwif *hwif; struct msix_entry *msix_entries; @@ -307,7 +343,11 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size); -int hinic_hwdev_ifup(struct hinic_hwdev *hwdev); +int hinic_hilink_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_hilink_cmd cmd, + void *buf_in, u16 in_size, void *buf_out, + u16 *out_size); + +int hinic_hwdev_ifup(struct hinic_hwdev *hwdev, u16 sq_depth, u16 rq_depth); void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index 3fbd2eb80582..cf127d896ba6 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -10,6 +10,7 @@ #include <linux/io.h> #include <linux/types.h> #include <linux/bitops.h> +#include <linux/delay.h> #include "hinic_hw_csr.h" #include "hinic_hw_if.h" @@ -18,6 +19,8 @@ #define VALID_MSIX_IDX(attr, msix_index) ((msix_index) < (attr)->num_irqs) +#define WAIT_HWIF_READY_TIMEOUT 10000 + /** * hinic_msix_attr_set - set message attribute for msix entry * @hwif: the HW interface of a pci function device @@ -187,20 +190,39 @@ void hinic_set_msix_state(struct hinic_hwif *hwif, u16 msix_idx, **/ static int hwif_ready(struct hinic_hwif *hwif) { - struct pci_dev *pdev = hwif->pdev; u32 addr, attr1; addr = HINIC_CSR_FUNC_ATTR1_ADDR; attr1 = hinic_hwif_read_reg(hwif, addr); - if (!HINIC_FA1_GET(attr1, INIT_STATUS)) { - dev_err(&pdev->dev, "hwif status is not ready\n"); - return -EFAULT; + if (!HINIC_FA1_GET(attr1, MGMT_INIT_STATUS)) + return -EBUSY; + + if (HINIC_IS_VF(hwif)) { + if (!HINIC_FA1_GET(attr1, PF_INIT_STATUS)) + return -EBUSY; } return 0; } +static int wait_hwif_ready(struct hinic_hwif *hwif) +{ + unsigned long timeout = 0; + + do { + if (!hwif_ready(hwif)) + return 0; + + usleep_range(999, 1000); + timeout++; + } while (timeout <= WAIT_HWIF_READY_TIMEOUT); + + dev_err(&hwif->pdev->dev, "Wait for hwif timeout\n"); + + return -EBUSY; +} + /** * set_hwif_attr - set the attributes in the relevant members in hwif * @hwif: the HW interface of a pci function device @@ -373,7 +395,7 @@ int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev) goto err_map_intr_bar; } - err = hwif_ready(hwif); + err = wait_hwif_ready(hwif); if (err) { dev_err(&pdev->dev, "HW interface is not ready\n"); goto err_hwif_ready; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index 53bb89c1dd26..0872e035faa1 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -55,13 +55,15 @@ #define HINIC_FA1_IRQS_PER_FUNC_SHIFT 20 #define HINIC_FA1_DMA_ATTR_PER_FUNC_SHIFT 24 /* reserved members - off 27 */ -#define HINIC_FA1_INIT_STATUS_SHIFT 30 +#define HINIC_FA1_MGMT_INIT_STATUS_SHIFT 30 +#define HINIC_FA1_PF_INIT_STATUS_SHIFT 31 #define HINIC_FA1_AEQS_PER_FUNC_MASK 0x3 #define HINIC_FA1_CEQS_PER_FUNC_MASK 0x7 #define HINIC_FA1_IRQS_PER_FUNC_MASK 0xF #define HINIC_FA1_DMA_ATTR_PER_FUNC_MASK 0x7 -#define HINIC_FA1_INIT_STATUS_MASK 0x1 +#define HINIC_FA1_MGMT_INIT_STATUS_MASK 0x1 +#define HINIC_FA1_PF_INIT_STATUS_MASK 0x1 #define HINIC_FA1_GET(val, member) \ (((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK) @@ -190,7 +192,7 @@ enum hinic_mod_type { HINIC_MOD_COMM = 0, /* HW communication module */ HINIC_MOD_L2NIC = 1, /* L2NIC module */ HINIC_MOD_CFGM = 7, /* Configuration module */ - + HINIC_MOD_HILINK = 14, /* Hilink module */ HINIC_MOD_MAX = 15 }; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c index a4581c988a63..3e3fa742e476 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -282,7 +282,7 @@ static int init_qp(struct hinic_func_to_io *func_to_io, err = hinic_wq_allocate(&func_to_io->wqs, &func_to_io->sq_wq[q_id], HINIC_SQ_WQEBB_SIZE, HINIC_SQ_PAGE_SIZE, - HINIC_SQ_DEPTH, HINIC_SQ_WQE_MAX_SIZE); + func_to_io->sq_depth, HINIC_SQ_WQE_MAX_SIZE); if (err) { dev_err(&pdev->dev, "Failed to allocate WQ for SQ\n"); return err; @@ -290,7 +290,7 @@ static int init_qp(struct hinic_func_to_io *func_to_io, err = hinic_wq_allocate(&func_to_io->wqs, &func_to_io->rq_wq[q_id], HINIC_RQ_WQEBB_SIZE, HINIC_RQ_PAGE_SIZE, - HINIC_RQ_DEPTH, HINIC_RQ_WQE_SIZE); + func_to_io->rq_depth, HINIC_RQ_WQE_SIZE); if (err) { dev_err(&pdev->dev, "Failed to allocate WQ for RQ\n"); goto err_rq_alloc; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h index 28c0594f636d..214f162f7579 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -60,6 +60,9 @@ struct hinic_func_to_io { struct hinic_qp *qps; u16 max_qps; + u16 sq_depth; + u16 rq_depth; + void __iomem **sq_db; void __iomem *db_base; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c index 564fb2294a29..bc2f87e6cb5d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mbox.c @@ -627,7 +627,7 @@ wait_for_mbox_seg_completion(struct hinic_mbox_func_to_func *func_to_func, struct hinic_hwdev *hwdev = func_to_func->hwdev; struct completion *done = &send_mbox->send_done; u32 cnt = 0; - ulong jif; + unsigned long jif; if (poll) { while (cnt < MBOX_MSG_POLLING_TIMEOUT) { @@ -869,7 +869,7 @@ int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, { struct hinic_recv_mbox *mbox_for_resp; struct mbox_msg_info msg_info = {0}; - ulong timeo; + unsigned long timeo; int err; mbox_for_resp = &func_to_func->mbox_resp[dst_func]; diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c index 20c5c8ea452e..fcf7bfe4aa47 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -643,6 +643,7 @@ void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size, /* increment prod_idx to the next */ prod_idx += ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + prod_idx = SQ_MASKED_IDX(sq, prod_idx); wmb(); /* Write all before the doorbell */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h index c30d092e48d5..ca3e2d060284 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -44,6 +44,9 @@ #define HINIC_SQ_DEPTH SZ_4K #define HINIC_RQ_DEPTH SZ_4K +#define HINIC_MAX_QUEUE_DEPTH SZ_4K +#define HINIC_MIN_QUEUE_DEPTH 128 + /* In any change to HINIC_RX_BUF_SZ, HINIC_RX_BUF_SZ_IDX must be changed */ #define HINIC_RX_BUF_SZ 2048 #define HINIC_RX_BUF_SZ_IDX HINIC_RX_BUF_SZ_2048_IDX diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index b66bb86cff96..e3ff119fe341 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -372,14 +372,15 @@ static void hinic_enable_rss(struct hinic_dev *nic_dev) netif_err(nic_dev, drv, netdev, "Failed to init rss\n"); } -static int hinic_open(struct net_device *netdev) +int hinic_open(struct net_device *netdev) { struct hinic_dev *nic_dev = netdev_priv(netdev); enum hinic_port_link_state link_state; int err, ret; if (!(nic_dev->flags & HINIC_INTF_UP)) { - err = hinic_hwdev_ifup(nic_dev->hwdev); + err = hinic_hwdev_ifup(nic_dev->hwdev, nic_dev->sq_depth, + nic_dev->rq_depth); if (err) { netif_err(nic_dev, drv, netdev, "Failed - HW interface up\n"); @@ -427,10 +428,6 @@ static int hinic_open(struct net_device *netdev) goto err_func_port_state; } - if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) - /* Wait up to 3 sec between port enable to link state */ - msleep(3000); - down(&nic_dev->mgmt_lock); err = hinic_port_link_state(nic_dev, &link_state); @@ -487,7 +484,7 @@ err_create_txqs: return err; } -static int hinic_close(struct net_device *netdev) +int hinic_close(struct net_device *netdev) { struct hinic_dev *nic_dev = netdev_priv(netdev); unsigned int flags; @@ -766,10 +763,12 @@ static void hinic_set_rx_mode(struct net_device *netdev) HINIC_RX_MODE_MC | HINIC_RX_MODE_BC; - if (netdev->flags & IFF_PROMISC) - rx_mode |= HINIC_RX_MODE_PROMISC; - else if (netdev->flags & IFF_ALLMULTI) + if (netdev->flags & IFF_PROMISC) { + if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) + rx_mode |= HINIC_RX_MODE_PROMISC; + } else if (netdev->flags & IFF_ALLMULTI) { rx_mode |= HINIC_RX_MODE_MC_ALL; + } rx_mode_work->rx_mode = rx_mode; @@ -868,6 +867,9 @@ static const struct net_device_ops hinic_netdev_ops = { .ndo_set_vf_vlan = hinic_ndo_set_vf_vlan, .ndo_get_vf_config = hinic_ndo_get_vf_config, .ndo_set_vf_trust = hinic_ndo_set_vf_trust, + .ndo_set_vf_rate = hinic_ndo_set_vf_bw, + .ndo_set_vf_spoofchk = hinic_ndo_set_vf_spoofchk, + .ndo_set_vf_link_state = hinic_ndo_set_vf_link_state, }; static const struct net_device_ops hinicvf_netdev_ops = { @@ -1037,6 +1039,8 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->rxqs = NULL; nic_dev->tx_weight = tx_weight; nic_dev->rx_weight = rx_weight; + nic_dev->sq_depth = HINIC_SQ_DEPTH; + nic_dev->rq_depth = HINIC_RQ_DEPTH; nic_dev->sriov_info.hwdev = hwdev; nic_dev->sriov_info.pdev = pdev; @@ -1232,6 +1236,8 @@ static void hinic_remove(struct pci_dev *pdev) unregister_netdev(netdev); + hinic_port_del_mac(nic_dev, netdev->dev_addr, 0); + hinic_hwdev_cb_unregister(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index b7fe0adcc29a..175c0ee00038 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -66,15 +66,15 @@ static int change_mac(struct hinic_dev *nic_dev, const u8 *addr, return -EFAULT; } - if (cmd == HINIC_PORT_CMD_SET_MAC && port_mac_cmd.status == - HINIC_PF_SET_VF_ALREADY) { - dev_warn(&pdev->dev, "PF has already set VF mac, Ignore set operation\n"); + if (port_mac_cmd.status == HINIC_PF_SET_VF_ALREADY) { + dev_warn(&pdev->dev, "PF has already set VF mac, ignore %s operation\n", + (op == MAC_SET) ? "set" : "del"); return HINIC_PF_SET_VF_ALREADY; } if (cmd == HINIC_PORT_CMD_SET_MAC && port_mac_cmd.status == HINIC_MGMT_STATUS_EXIST) - dev_warn(&pdev->dev, "MAC is repeated. Ignore set operation\n"); + dev_warn(&pdev->dev, "MAC is repeated, ignore set operation\n"); return 0; } @@ -473,7 +473,7 @@ int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs) rq_num.func_id = HINIC_HWIF_FUNC_IDX(hwif); rq_num.num_rqs = num_rqs; - rq_num.rq_depth = ilog2(HINIC_SQ_DEPTH); + rq_num.rq_depth = ilog2(nic_dev->rq_depth); err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RQ_IQ_MAP, &rq_num, sizeof(rq_num), @@ -1072,3 +1072,132 @@ int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver) return 0; } + +int hinic_get_link_mode(struct hinic_hwdev *hwdev, + struct hinic_link_mode_cmd *link_mode) +{ + u16 out_size; + int err; + + if (!hwdev || !link_mode) + return -EINVAL; + + out_size = sizeof(*link_mode); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_MODE, + link_mode, sizeof(*link_mode), + link_mode, &out_size); + if (err || !out_size || link_mode->status) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to get link mode, err: %d, status: 0x%x, out size: 0x%x\n", + err, link_mode->status, out_size); + return -EIO; + } + + return 0; +} + +int hinic_set_autoneg(struct hinic_hwdev *hwdev, bool enable) +{ + struct hinic_set_autoneg_cmd autoneg = {0}; + u16 out_size = sizeof(autoneg); + int err; + + if (!hwdev) + return -EINVAL; + + autoneg.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + autoneg.enable = enable; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_AUTONEG, + &autoneg, sizeof(autoneg), + &autoneg, &out_size); + if (err || !out_size || autoneg.status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to %s autoneg, err: %d, status: 0x%x, out size: 0x%x\n", + enable ? "enable" : "disable", err, autoneg.status, + out_size); + return -EIO; + } + + return 0; +} + +int hinic_set_speed(struct hinic_hwdev *hwdev, enum nic_speed_level speed) +{ + struct hinic_speed_cmd speed_info = {0}; + u16 out_size = sizeof(speed_info); + int err; + + if (!hwdev) + return -EINVAL; + + speed_info.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + speed_info.speed = speed; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_SPEED, + &speed_info, sizeof(speed_info), + &speed_info, &out_size); + if (err || !out_size || speed_info.status) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to set speed, err: %d, status: 0x%x, out size: 0x%x\n", + err, speed_info.status, out_size); + return -EIO; + } + + return 0; +} + +int hinic_set_link_settings(struct hinic_hwdev *hwdev, + struct hinic_link_ksettings_info *info) +{ + u16 out_size = sizeof(*info); + int err; + + err = hinic_hilink_msg_cmd(hwdev, HINIC_HILINK_CMD_SET_LINK_SETTINGS, + info, sizeof(*info), info, &out_size); + if ((info->status != HINIC_MGMT_CMD_UNSUPPORTED && + info->status) || err || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, + "Failed to set link settings, err: %d, status: 0x%x, out size: 0x%x\n", + err, info->status, out_size); + return -EFAULT; + } + + return info->status; +} + +int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev, + struct hinic_pause_config *pause_info) +{ + u16 out_size = sizeof(*pause_info); + int err; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO, + pause_info, sizeof(*pause_info), + pause_info, &out_size); + if (err || !out_size || pause_info->status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to get pause info, err: %d, status: 0x%x, out size: 0x%x\n", + err, pause_info->status, out_size); + return -EIO; + } + + return 0; +} + +int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, + struct hinic_pause_config *pause_info) +{ + u16 out_size = sizeof(*pause_info); + int err; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO, + pause_info, sizeof(*pause_info), + pause_info, &out_size); + if (err || !out_size || pause_info->status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to set pause info, err: %d, status: 0x%x, out size: 0x%x\n", + err, pause_info->status, out_size); + return -EIO; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 5ad04fb6722a..661c6322dc15 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -79,6 +79,42 @@ enum hinic_speed { HINIC_SPEED_UNKNOWN = 0xFF, }; +enum hinic_link_mode { + HINIC_10GE_BASE_KR = 0, + HINIC_40GE_BASE_KR4 = 1, + HINIC_40GE_BASE_CR4 = 2, + HINIC_100GE_BASE_KR4 = 3, + HINIC_100GE_BASE_CR4 = 4, + HINIC_25GE_BASE_KR_S = 5, + HINIC_25GE_BASE_CR_S = 6, + HINIC_25GE_BASE_KR = 7, + HINIC_25GE_BASE_CR = 8, + HINIC_GE_BASE_KX = 9, + HINIC_LINK_MODE_NUMBERS, + + HINIC_SUPPORTED_UNKNOWN = 0xFFFF, +}; + +enum hinic_port_type { + HINIC_PORT_TP, /* BASET */ + HINIC_PORT_AUI, + HINIC_PORT_MII, + HINIC_PORT_FIBRE, /* OPTICAL */ + HINIC_PORT_BNC, + HINIC_PORT_ELEC, + HINIC_PORT_COPPER, /* PORT_DA */ + HINIC_PORT_AOC, + HINIC_PORT_BACKPLANE, + HINIC_PORT_NONE = 0xEF, + HINIC_PORT_OTHER = 0xFF, +}; + +enum hinic_valid_link_settings { + HILINK_LINK_SET_SPEED = 0x1, + HILINK_LINK_SET_AUTONEG = 0x2, + HILINK_LINK_SET_FEC = 0x4, +}; + enum hinic_tso_state { HINIC_TSO_DISABLE = 0, HINIC_TSO_ENABLE = 1, @@ -179,6 +215,50 @@ struct hinic_port_cap { u8 rsvd2[3]; }; +struct hinic_link_mode_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + u16 supported; /* 0xFFFF represents invalid value */ + u16 advertised; +}; + +struct hinic_speed_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 speed; +}; + +struct hinic_set_autoneg_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 enable; /* 1: enable , 0: disable */ +}; + +struct hinic_link_ksettings_info { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + + u32 valid_bitmap; + u32 speed; /* enum nic_speed_level */ + u8 autoneg; /* 0 - off; 1 - on */ + u8 fec; /* 0 - RSFEC; 1 - BASEFEC; 2 - NOFEC */ + u8 rsvd2[18]; /* reserved for duplex, port, etc. */ +}; + struct hinic_tso_config { u8 status; u8 version; @@ -506,6 +586,61 @@ struct hinic_cmd_vport_stats { struct hinic_vport_stats stats; }; +struct hinic_tx_rate_cfg_max_min { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + u32 min_rate; + u32 max_rate; + u8 rsvd2[8]; +}; + +struct hinic_tx_rate_cfg { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + u32 tx_rate; +}; + +enum nic_speed_level { + LINK_SPEED_10MB = 0, + LINK_SPEED_100MB, + LINK_SPEED_1GB, + LINK_SPEED_10GB, + LINK_SPEED_25GB, + LINK_SPEED_40GB, + LINK_SPEED_100GB, + LINK_SPEED_LEVELS, +}; + +struct hinic_spoofchk_set { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 state; + u8 rsvd1; + u16 func_id; +}; + +struct hinic_pause_config { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_id; + u16 rsvd1; + u32 auto_neg; + u32 rx_pause; + u32 tx_pause; +}; + int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, u16 vlan_id); @@ -585,4 +720,24 @@ int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en); int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver); +int hinic_set_link_settings(struct hinic_hwdev *hwdev, + struct hinic_link_ksettings_info *info); + +int hinic_get_link_mode(struct hinic_hwdev *hwdev, + struct hinic_link_mode_cmd *link_mode); + +int hinic_set_autoneg(struct hinic_hwdev *hwdev, bool enable); + +int hinic_set_speed(struct hinic_hwdev *hwdev, enum nic_speed_level speed); + +int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev, + struct hinic_pause_config *pause_info); + +int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, + struct hinic_pause_config *pause_info); + +int hinic_open(struct net_device *netdev); + +int hinic_close(struct net_device *netdev); + #endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c index fd4aaf43874a..efab2dd2c889 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.c @@ -22,6 +22,7 @@ MODULE_PARM_DESC(set_vf_link_state, "Set vf link state, 0 represents link auto, #define HINIC_VLAN_PRIORITY_SHIFT 13 #define HINIC_ADD_VLAN_IN_MAC 0x8000 +#define HINIC_TX_RATE_TABLE_FULL 12 static int hinic_set_mac(struct hinic_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id, u16 func_id) @@ -129,6 +130,84 @@ static int hinic_set_vf_vlan(struct hinic_hwdev *hwdev, bool add, u16 vid, return 0; } +static int hinic_set_vf_tx_rate_max_min(struct hinic_hwdev *hwdev, u16 vf_id, + u32 max_rate, u32 min_rate) +{ + struct hinic_func_to_io *nic_io = &hwdev->func_to_io; + struct hinic_tx_rate_cfg_max_min rate_cfg = {0}; + u16 out_size = sizeof(rate_cfg); + int err; + + rate_cfg.func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id; + rate_cfg.max_rate = max_rate; + rate_cfg.min_rate = min_rate; + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_VF_MAX_MIN_RATE, + &rate_cfg, sizeof(rate_cfg), &rate_cfg, + &out_size); + if ((rate_cfg.status != HINIC_MGMT_CMD_UNSUPPORTED && + rate_cfg.status) || err || !out_size) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to set VF(%d) max rate(%d), min rate(%d), err: %d, status: 0x%x, out size: 0x%x\n", + HW_VF_ID_TO_OS(vf_id), max_rate, min_rate, err, + rate_cfg.status, out_size); + return -EIO; + } + + if (!rate_cfg.status) { + nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].max_rate = max_rate; + nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].min_rate = min_rate; + } + + return rate_cfg.status; +} + +static int hinic_set_vf_rate_limit(struct hinic_hwdev *hwdev, u16 vf_id, + u32 tx_rate) +{ + struct hinic_func_to_io *nic_io = &hwdev->func_to_io; + struct hinic_tx_rate_cfg rate_cfg = {0}; + u16 out_size = sizeof(rate_cfg); + int err; + + rate_cfg.func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id; + rate_cfg.tx_rate = tx_rate; + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_VF_RATE, + &rate_cfg, sizeof(rate_cfg), &rate_cfg, + &out_size); + if (err || !out_size || rate_cfg.status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to set VF(%d) rate(%d), err: %d, status: 0x%x, out size: 0x%x\n", + HW_VF_ID_TO_OS(vf_id), tx_rate, err, rate_cfg.status, + out_size); + if (rate_cfg.status) + return rate_cfg.status; + + return -EIO; + } + + nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].max_rate = tx_rate; + nic_io->vf_infos[HW_VF_ID_TO_OS(vf_id)].min_rate = 0; + + return 0; +} + +static int hinic_set_vf_tx_rate(struct hinic_hwdev *hwdev, u16 vf_id, + u32 max_rate, u32 min_rate) +{ + int err; + + err = hinic_set_vf_tx_rate_max_min(hwdev, vf_id, max_rate, min_rate); + if (err != HINIC_MGMT_CMD_UNSUPPORTED) + return err; + + if (min_rate) { + dev_err(&hwdev->hwif->pdev->dev, "Current firmware doesn't support to set min tx rate\n"); + return -EOPNOTSUPP; + } + + dev_info(&hwdev->hwif->pdev->dev, "Current firmware doesn't support to set min tx rate, force min_tx_rate = max_tx_rate\n"); + + return hinic_set_vf_rate_limit(hwdev, vf_id, max_rate); +} + static int hinic_init_vf_config(struct hinic_hwdev *hwdev, u16 vf_id) { struct vf_data_storage *vf_info; @@ -160,6 +239,17 @@ static int hinic_init_vf_config(struct hinic_hwdev *hwdev, u16 vf_id) } } + if (vf_info->max_rate) { + err = hinic_set_vf_tx_rate(hwdev, vf_id, vf_info->max_rate, + vf_info->min_rate); + if (err) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to set VF %d max rate: %d, min rate: %d\n", + HW_VF_ID_TO_OS(vf_id), vf_info->max_rate, + vf_info->min_rate); + return err; + } + } + return 0; } @@ -700,6 +790,185 @@ int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting) return err; } +int hinic_ndo_set_vf_bw(struct net_device *netdev, + int vf, int min_tx_rate, int max_tx_rate) +{ + u32 speeds[] = {SPEED_10, SPEED_100, SPEED_1000, SPEED_10000, + SPEED_25000, SPEED_40000, SPEED_100000}; + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_port_cap port_cap = { 0 }; + enum hinic_port_link_state link_state; + int err; + + if (vf >= nic_dev->sriov_info.num_vfs) { + netif_err(nic_dev, drv, netdev, "VF number must be less than %d\n", + nic_dev->sriov_info.num_vfs); + return -EINVAL; + } + + if (max_tx_rate < min_tx_rate) { + netif_err(nic_dev, drv, netdev, "Max rate %d must be greater than or equal to min rate %d\n", + max_tx_rate, min_tx_rate); + return -EINVAL; + } + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) { + netif_err(nic_dev, drv, netdev, + "Get link status failed when setting vf tx rate\n"); + return -EIO; + } + + if (link_state == HINIC_LINK_STATE_DOWN) { + netif_err(nic_dev, drv, netdev, + "Link status must be up when setting vf tx rate\n"); + return -EPERM; + } + + err = hinic_port_get_cap(nic_dev, &port_cap); + if (err || port_cap.speed > LINK_SPEED_100GB) + return -EIO; + + /* rate limit cannot be less than 0 and greater than link speed */ + if (max_tx_rate < 0 || max_tx_rate > speeds[port_cap.speed]) { + netif_err(nic_dev, drv, netdev, "Max tx rate must be in [0 - %d]\n", + speeds[port_cap.speed]); + return -EINVAL; + } + + err = hinic_set_vf_tx_rate(nic_dev->hwdev, OS_VF_ID_TO_HW(vf), + max_tx_rate, min_tx_rate); + if (err) { + netif_err(nic_dev, drv, netdev, + "Unable to set VF %d max rate %d min rate %d%s\n", + vf, max_tx_rate, min_tx_rate, + err == HINIC_TX_RATE_TABLE_FULL ? + ", tx rate profile is full" : ""); + return -EIO; + } + + netif_info(nic_dev, drv, netdev, + "Set VF %d max tx rate %d min tx rate %d successfully\n", + vf, max_tx_rate, min_tx_rate); + + return 0; +} + +static int hinic_set_vf_spoofchk(struct hinic_hwdev *hwdev, u16 vf_id, + bool spoofchk) +{ + struct hinic_spoofchk_set spoofchk_cfg = {0}; + struct vf_data_storage *vf_infos = NULL; + u16 out_size = sizeof(spoofchk_cfg); + int err; + + if (!hwdev) + return -EINVAL; + + vf_infos = hwdev->func_to_io.vf_infos; + + spoofchk_cfg.func_id = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id; + spoofchk_cfg.state = spoofchk ? 1 : 0; + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_ENABLE_SPOOFCHK, + &spoofchk_cfg, sizeof(spoofchk_cfg), + &spoofchk_cfg, &out_size); + if (spoofchk_cfg.status == HINIC_MGMT_CMD_UNSUPPORTED) { + err = HINIC_MGMT_CMD_UNSUPPORTED; + } else if (err || !out_size || spoofchk_cfg.status) { + dev_err(&hwdev->hwif->pdev->dev, "Failed to set VF(%d) spoofchk, err: %d, status: 0x%x, out size: 0x%x\n", + HW_VF_ID_TO_OS(vf_id), err, spoofchk_cfg.status, + out_size); + err = -EIO; + } + + vf_infos[HW_VF_ID_TO_OS(vf_id)].spoofchk = spoofchk; + + return err; +} + +int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_sriov_info *sriov_info; + bool cur_spoofchk; + int err; + + sriov_info = &nic_dev->sriov_info; + if (vf >= sriov_info->num_vfs) + return -EINVAL; + + cur_spoofchk = nic_dev->hwdev->func_to_io.vf_infos[vf].spoofchk; + + /* same request, so just return success */ + if ((setting && cur_spoofchk) || (!setting && !cur_spoofchk)) + return 0; + + 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"); + } else if (err == HINIC_MGMT_CMD_UNSUPPORTED) { + netif_err(nic_dev, drv, netdev, + "Current firmware doesn't support to set vf spoofchk, need to upgrade latest firmware version\n"); + err = -EOPNOTSUPP; + } + + return err; +} + +static int hinic_set_vf_link_state(struct hinic_hwdev *hwdev, u16 vf_id, + int link) +{ + struct hinic_func_to_io *nic_io = &hwdev->func_to_io; + struct vf_data_storage *vf_infos = nic_io->vf_infos; + u8 link_status = 0; + + switch (link) { + case HINIC_IFLA_VF_LINK_STATE_AUTO: + vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced = false; + vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up = nic_io->link_status ? + true : false; + link_status = nic_io->link_status; + break; + case HINIC_IFLA_VF_LINK_STATE_ENABLE: + vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced = true; + vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up = true; + link_status = HINIC_LINK_UP; + break; + case HINIC_IFLA_VF_LINK_STATE_DISABLE: + vf_infos[HW_VF_ID_TO_OS(vf_id)].link_forced = true; + vf_infos[HW_VF_ID_TO_OS(vf_id)].link_up = false; + link_status = HINIC_LINK_DOWN; + break; + default: + return -EINVAL; + } + + /* Notify the VF of its new link state */ + hinic_notify_vf_link_status(hwdev, vf_id, link_status); + + return 0; +} + +int hinic_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_sriov_info *sriov_info; + + sriov_info = &nic_dev->sriov_info; + + if (vf_id >= sriov_info->num_vfs) { + netif_err(nic_dev, drv, netdev, + "Invalid VF Identifier %d\n", vf_id); + return -EINVAL; + } + + return hinic_set_vf_link_state(sriov_info->hwdev, + OS_VF_ID_TO_HW(vf_id), link); +} + /* pf receive message from vf */ static int nic_pf_mbox_handler(void *hwdev, u16 vf_id, u8 cmd, void *buf_in, u16 in_size, void *buf_out, u16 *out_size) @@ -801,6 +1070,12 @@ static void hinic_clear_vf_infos(struct hinic_dev *nic_dev, u16 vf_id) if (hinic_vf_info_vlanprio(nic_dev->hwdev, vf_id)) hinic_kill_vf_vlan(nic_dev->hwdev, vf_id); + if (vf_infos->max_rate) + hinic_set_vf_tx_rate(nic_dev->hwdev, vf_id, 0, 0); + + if (vf_infos->spoofchk) + hinic_set_vf_spoofchk(nic_dev->hwdev, vf_id, false); + if (vf_infos->trust) hinic_set_vf_trust(nic_dev->hwdev, vf_id, false); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_sriov.h b/drivers/net/ethernet/huawei/hinic/hinic_sriov.h index 64affc7474b5..ba627a362f9a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_sriov.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_sriov.h @@ -86,6 +86,13 @@ int hinic_ndo_get_vf_config(struct net_device *netdev, int hinic_ndo_set_vf_trust(struct net_device *netdev, int vf, bool setting); +int hinic_ndo_set_vf_bw(struct net_device *netdev, + int vf, int min_tx_rate, int max_tx_rate); + +int hinic_ndo_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); + +int hinic_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link); + void hinic_notify_all_vfs_link_changed(struct hinic_hwdev *hwdev, u8 link_status); |