162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Huawei HiNIC PCI Express Linux driver 362306a36Sopenharmony_ci * Copyright(c) 2017 Huawei Technologies Co., Ltd 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 662306a36Sopenharmony_ci * under the terms and conditions of the GNU General Public License, 762306a36Sopenharmony_ci * version 2, as published by the Free Software Foundation. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is distributed in the hope it will be useful, but WITHOUT 1062306a36Sopenharmony_ci * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1262306a36Sopenharmony_ci * for more details. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/types.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/etherdevice.h> 2462306a36Sopenharmony_ci#include <linux/netdevice.h> 2562306a36Sopenharmony_ci#include <linux/if_vlan.h> 2662306a36Sopenharmony_ci#include <linux/ethtool.h> 2762306a36Sopenharmony_ci#include <linux/vmalloc.h> 2862306a36Sopenharmony_ci#include <linux/sfp.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "hinic_hw_qp.h" 3162306a36Sopenharmony_ci#include "hinic_hw_dev.h" 3262306a36Sopenharmony_ci#include "hinic_port.h" 3362306a36Sopenharmony_ci#include "hinic_tx.h" 3462306a36Sopenharmony_ci#include "hinic_rx.h" 3562306a36Sopenharmony_ci#include "hinic_dev.h" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define SET_LINK_STR_MAX_LEN 16 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define GET_SUPPORTED_MODE 0 4062306a36Sopenharmony_ci#define GET_ADVERTISED_MODE 1 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define ETHTOOL_ADD_SUPPORTED_SPEED_LINK_MODE(ecmd, mode) \ 4362306a36Sopenharmony_ci ((ecmd)->supported |= \ 4462306a36Sopenharmony_ci (1UL << hw_to_ethtool_link_mode_table[mode].link_mode_bit)) 4562306a36Sopenharmony_ci#define ETHTOOL_ADD_ADVERTISED_SPEED_LINK_MODE(ecmd, mode) \ 4662306a36Sopenharmony_ci ((ecmd)->advertising |= \ 4762306a36Sopenharmony_ci (1UL << hw_to_ethtool_link_mode_table[mode].link_mode_bit)) 4862306a36Sopenharmony_ci#define ETHTOOL_ADD_SUPPORTED_LINK_MODE(ecmd, mode) \ 4962306a36Sopenharmony_ci ((ecmd)->supported |= SUPPORTED_##mode) 5062306a36Sopenharmony_ci#define ETHTOOL_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \ 5162306a36Sopenharmony_ci ((ecmd)->advertising |= ADVERTISED_##mode) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define COALESCE_PENDING_LIMIT_UNIT 8 5462306a36Sopenharmony_ci#define COALESCE_TIMER_CFG_UNIT 9 5562306a36Sopenharmony_ci#define COALESCE_ALL_QUEUE 0xFFFF 5662306a36Sopenharmony_ci#define COALESCE_MAX_PENDING_LIMIT (255 * COALESCE_PENDING_LIMIT_UNIT) 5762306a36Sopenharmony_ci#define COALESCE_MAX_TIMER_CFG (255 * COALESCE_TIMER_CFG_UNIT) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct hw2ethtool_link_mode { 6062306a36Sopenharmony_ci enum ethtool_link_mode_bit_indices link_mode_bit; 6162306a36Sopenharmony_ci u32 speed; 6262306a36Sopenharmony_ci enum hinic_link_mode hw_link_mode; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct cmd_link_settings { 6662306a36Sopenharmony_ci u64 supported; 6762306a36Sopenharmony_ci u64 advertising; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci u32 speed; 7062306a36Sopenharmony_ci u8 duplex; 7162306a36Sopenharmony_ci u8 port; 7262306a36Sopenharmony_ci u8 autoneg; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic u32 hw_to_ethtool_speed[LINK_SPEED_LEVELS] = { 7662306a36Sopenharmony_ci SPEED_10, SPEED_100, 7762306a36Sopenharmony_ci SPEED_1000, SPEED_10000, 7862306a36Sopenharmony_ci SPEED_25000, SPEED_40000, 7962306a36Sopenharmony_ci SPEED_100000 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct hw2ethtool_link_mode 8362306a36Sopenharmony_ci hw_to_ethtool_link_mode_table[HINIC_LINK_MODE_NUMBERS] = { 8462306a36Sopenharmony_ci { 8562306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 8662306a36Sopenharmony_ci .speed = SPEED_10000, 8762306a36Sopenharmony_ci .hw_link_mode = HINIC_10GE_BASE_KR, 8862306a36Sopenharmony_ci }, 8962306a36Sopenharmony_ci { 9062306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, 9162306a36Sopenharmony_ci .speed = SPEED_40000, 9262306a36Sopenharmony_ci .hw_link_mode = HINIC_40GE_BASE_KR4, 9362306a36Sopenharmony_ci }, 9462306a36Sopenharmony_ci { 9562306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, 9662306a36Sopenharmony_ci .speed = SPEED_40000, 9762306a36Sopenharmony_ci .hw_link_mode = HINIC_40GE_BASE_CR4, 9862306a36Sopenharmony_ci }, 9962306a36Sopenharmony_ci { 10062306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, 10162306a36Sopenharmony_ci .speed = SPEED_100000, 10262306a36Sopenharmony_ci .hw_link_mode = HINIC_100GE_BASE_KR4, 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci { 10562306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, 10662306a36Sopenharmony_ci .speed = SPEED_100000, 10762306a36Sopenharmony_ci .hw_link_mode = HINIC_100GE_BASE_CR4, 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci { 11062306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, 11162306a36Sopenharmony_ci .speed = SPEED_25000, 11262306a36Sopenharmony_ci .hw_link_mode = HINIC_25GE_BASE_KR_S, 11362306a36Sopenharmony_ci }, 11462306a36Sopenharmony_ci { 11562306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, 11662306a36Sopenharmony_ci .speed = SPEED_25000, 11762306a36Sopenharmony_ci .hw_link_mode = HINIC_25GE_BASE_CR_S, 11862306a36Sopenharmony_ci }, 11962306a36Sopenharmony_ci { 12062306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, 12162306a36Sopenharmony_ci .speed = SPEED_25000, 12262306a36Sopenharmony_ci .hw_link_mode = HINIC_25GE_BASE_KR, 12362306a36Sopenharmony_ci }, 12462306a36Sopenharmony_ci { 12562306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, 12662306a36Sopenharmony_ci .speed = SPEED_25000, 12762306a36Sopenharmony_ci .hw_link_mode = HINIC_25GE_BASE_CR, 12862306a36Sopenharmony_ci }, 12962306a36Sopenharmony_ci { 13062306a36Sopenharmony_ci .link_mode_bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 13162306a36Sopenharmony_ci .speed = SPEED_1000, 13262306a36Sopenharmony_ci .hw_link_mode = HINIC_GE_BASE_KX, 13362306a36Sopenharmony_ci }, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define LP_DEFAULT_TIME 5 /* seconds */ 13762306a36Sopenharmony_ci#define LP_PKT_LEN 1514 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define PORT_DOWN_ERR_IDX 0 14062306a36Sopenharmony_cienum diag_test_index { 14162306a36Sopenharmony_ci INTERNAL_LP_TEST = 0, 14262306a36Sopenharmony_ci EXTERNAL_LP_TEST = 1, 14362306a36Sopenharmony_ci DIAG_TEST_MAX = 2, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void set_link_speed(struct ethtool_link_ksettings *link_ksettings, 14762306a36Sopenharmony_ci enum hinic_speed speed) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci switch (speed) { 15062306a36Sopenharmony_ci case HINIC_SPEED_10MB_LINK: 15162306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_10; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci case HINIC_SPEED_100MB_LINK: 15562306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_100; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci case HINIC_SPEED_1000MB_LINK: 15962306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_1000; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci case HINIC_SPEED_10GB_LINK: 16362306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_10000; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci case HINIC_SPEED_25GB_LINK: 16762306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_25000; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci case HINIC_SPEED_40GB_LINK: 17162306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_40000; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci case HINIC_SPEED_100GB_LINK: 17562306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_100000; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci default: 17962306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_UNKNOWN; 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int hinic_get_link_mode_index(enum hinic_link_mode link_mode) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci int i = 0; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < HINIC_LINK_MODE_NUMBERS; i++) { 18962306a36Sopenharmony_ci if (link_mode == hw_to_ethtool_link_mode_table[i].hw_link_mode) 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return i; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void hinic_add_ethtool_link_mode(struct cmd_link_settings *link_settings, 19762306a36Sopenharmony_ci enum hinic_link_mode hw_link_mode, 19862306a36Sopenharmony_ci u32 name) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci enum hinic_link_mode link_mode; 20162306a36Sopenharmony_ci int idx = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (link_mode = 0; link_mode < HINIC_LINK_MODE_NUMBERS; link_mode++) { 20462306a36Sopenharmony_ci if (hw_link_mode & ((u32)1 << link_mode)) { 20562306a36Sopenharmony_ci idx = hinic_get_link_mode_index(link_mode); 20662306a36Sopenharmony_ci if (idx >= HINIC_LINK_MODE_NUMBERS) 20762306a36Sopenharmony_ci continue; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (name == GET_SUPPORTED_MODE) 21062306a36Sopenharmony_ci ETHTOOL_ADD_SUPPORTED_SPEED_LINK_MODE 21162306a36Sopenharmony_ci (link_settings, idx); 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_SPEED_LINK_MODE 21462306a36Sopenharmony_ci (link_settings, idx); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void hinic_link_port_type(struct cmd_link_settings *link_settings, 22062306a36Sopenharmony_ci enum hinic_port_type port_type) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci switch (port_type) { 22362306a36Sopenharmony_ci case HINIC_PORT_ELEC: 22462306a36Sopenharmony_ci case HINIC_PORT_TP: 22562306a36Sopenharmony_ci ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, TP); 22662306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, TP); 22762306a36Sopenharmony_ci link_settings->port = PORT_TP; 22862306a36Sopenharmony_ci break; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci case HINIC_PORT_AOC: 23162306a36Sopenharmony_ci case HINIC_PORT_FIBRE: 23262306a36Sopenharmony_ci ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, FIBRE); 23362306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, FIBRE); 23462306a36Sopenharmony_ci link_settings->port = PORT_FIBRE; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci case HINIC_PORT_COPPER: 23862306a36Sopenharmony_ci ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, FIBRE); 23962306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, FIBRE); 24062306a36Sopenharmony_ci link_settings->port = PORT_DA; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci case HINIC_PORT_BACKPLANE: 24462306a36Sopenharmony_ci ETHTOOL_ADD_SUPPORTED_LINK_MODE(link_settings, Backplane); 24562306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(link_settings, Backplane); 24662306a36Sopenharmony_ci link_settings->port = PORT_NONE; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci default: 25062306a36Sopenharmony_ci link_settings->port = PORT_OTHER; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int hinic_get_link_ksettings(struct net_device *netdev, 25662306a36Sopenharmony_ci struct ethtool_link_ksettings 25762306a36Sopenharmony_ci *link_ksettings) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 26062306a36Sopenharmony_ci struct hinic_link_mode_cmd link_mode = { 0 }; 26162306a36Sopenharmony_ci struct hinic_pause_config pause_info = { 0 }; 26262306a36Sopenharmony_ci struct cmd_link_settings settings = { 0 }; 26362306a36Sopenharmony_ci enum hinic_port_link_state link_state; 26462306a36Sopenharmony_ci struct hinic_port_cap port_cap; 26562306a36Sopenharmony_ci int err; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); 26862306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_UNKNOWN; 27162306a36Sopenharmony_ci link_ksettings->base.autoneg = AUTONEG_DISABLE; 27262306a36Sopenharmony_ci link_ksettings->base.duplex = DUPLEX_UNKNOWN; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci err = hinic_port_get_cap(nic_dev, &port_cap); 27562306a36Sopenharmony_ci if (err) 27662306a36Sopenharmony_ci return err; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci hinic_link_port_type(&settings, port_cap.port_type); 27962306a36Sopenharmony_ci link_ksettings->base.port = settings.port; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci err = hinic_port_link_state(nic_dev, &link_state); 28262306a36Sopenharmony_ci if (err) 28362306a36Sopenharmony_ci return err; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (link_state == HINIC_LINK_STATE_UP) { 28662306a36Sopenharmony_ci set_link_speed(link_ksettings, port_cap.speed); 28762306a36Sopenharmony_ci link_ksettings->base.duplex = 28862306a36Sopenharmony_ci (port_cap.duplex == HINIC_DUPLEX_FULL) ? 28962306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!!(port_cap.autoneg_cap & HINIC_AUTONEG_SUPPORTED)) 29362306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 29462306a36Sopenharmony_ci advertising, Autoneg); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (port_cap.autoneg_state == HINIC_AUTONEG_ACTIVE) 29762306a36Sopenharmony_ci link_ksettings->base.autoneg = AUTONEG_ENABLE; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci err = hinic_get_link_mode(nic_dev->hwdev, &link_mode); 30062306a36Sopenharmony_ci if (err || link_mode.supported == HINIC_SUPPORTED_UNKNOWN || 30162306a36Sopenharmony_ci link_mode.advertised == HINIC_SUPPORTED_UNKNOWN) 30262306a36Sopenharmony_ci return -EIO; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci hinic_add_ethtool_link_mode(&settings, link_mode.supported, 30562306a36Sopenharmony_ci GET_SUPPORTED_MODE); 30662306a36Sopenharmony_ci hinic_add_ethtool_link_mode(&settings, link_mode.advertised, 30762306a36Sopenharmony_ci GET_ADVERTISED_MODE); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) { 31062306a36Sopenharmony_ci err = hinic_get_hw_pause_info(nic_dev->hwdev, &pause_info); 31162306a36Sopenharmony_ci if (err) 31262306a36Sopenharmony_ci return err; 31362306a36Sopenharmony_ci ETHTOOL_ADD_SUPPORTED_LINK_MODE(&settings, Pause); 31462306a36Sopenharmony_ci if (pause_info.rx_pause && pause_info.tx_pause) { 31562306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Pause); 31662306a36Sopenharmony_ci } else if (pause_info.tx_pause) { 31762306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Asym_Pause); 31862306a36Sopenharmony_ci } else if (pause_info.rx_pause) { 31962306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Pause); 32062306a36Sopenharmony_ci ETHTOOL_ADD_ADVERTISED_LINK_MODE(&settings, Asym_Pause); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci linkmode_copy(link_ksettings->link_modes.supported, 32562306a36Sopenharmony_ci (unsigned long *)&settings.supported); 32662306a36Sopenharmony_ci linkmode_copy(link_ksettings->link_modes.advertising, 32762306a36Sopenharmony_ci (unsigned long *)&settings.advertising); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int hinic_ethtool_to_hw_speed_level(u32 speed) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci int i; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci for (i = 0; i < LINK_SPEED_LEVELS; i++) { 33762306a36Sopenharmony_ci if (hw_to_ethtool_speed[i] == speed) 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return i; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic bool hinic_is_support_speed(enum hinic_link_mode supported_link, 34562306a36Sopenharmony_ci u32 speed) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci enum hinic_link_mode link_mode; 34862306a36Sopenharmony_ci int idx; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci for (link_mode = 0; link_mode < HINIC_LINK_MODE_NUMBERS; link_mode++) { 35162306a36Sopenharmony_ci if (!(supported_link & ((u32)1 << link_mode))) 35262306a36Sopenharmony_ci continue; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci idx = hinic_get_link_mode_index(link_mode); 35562306a36Sopenharmony_ci if (idx >= HINIC_LINK_MODE_NUMBERS) 35662306a36Sopenharmony_ci continue; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (hw_to_ethtool_link_mode_table[idx].speed == speed) 35962306a36Sopenharmony_ci return true; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return false; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic bool hinic_is_speed_legal(struct hinic_dev *nic_dev, u32 speed) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct hinic_link_mode_cmd link_mode = { 0 }; 36862306a36Sopenharmony_ci struct net_device *netdev = nic_dev->netdev; 36962306a36Sopenharmony_ci enum nic_speed_level speed_level = 0; 37062306a36Sopenharmony_ci int err; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci err = hinic_get_link_mode(nic_dev->hwdev, &link_mode); 37362306a36Sopenharmony_ci if (err) 37462306a36Sopenharmony_ci return false; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (link_mode.supported == HINIC_SUPPORTED_UNKNOWN || 37762306a36Sopenharmony_ci link_mode.advertised == HINIC_SUPPORTED_UNKNOWN) 37862306a36Sopenharmony_ci return false; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci speed_level = hinic_ethtool_to_hw_speed_level(speed); 38162306a36Sopenharmony_ci if (speed_level >= LINK_SPEED_LEVELS || 38262306a36Sopenharmony_ci !hinic_is_support_speed(link_mode.supported, speed)) { 38362306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 38462306a36Sopenharmony_ci "Unsupported speed: %d\n", speed); 38562306a36Sopenharmony_ci return false; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return true; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int get_link_settings_type(struct hinic_dev *nic_dev, 39262306a36Sopenharmony_ci u8 autoneg, u32 speed, u32 *set_settings) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct hinic_port_cap port_cap = { 0 }; 39562306a36Sopenharmony_ci int err; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci err = hinic_port_get_cap(nic_dev, &port_cap); 39862306a36Sopenharmony_ci if (err) 39962306a36Sopenharmony_ci return err; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* always set autonegotiation */ 40262306a36Sopenharmony_ci if (port_cap.autoneg_cap) 40362306a36Sopenharmony_ci *set_settings |= HILINK_LINK_SET_AUTONEG; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (autoneg == AUTONEG_ENABLE) { 40662306a36Sopenharmony_ci if (!port_cap.autoneg_cap) { 40762306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, "Not support autoneg\n"); 40862306a36Sopenharmony_ci return -EOPNOTSUPP; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci } else if (speed != (u32)SPEED_UNKNOWN) { 41162306a36Sopenharmony_ci /* set speed only when autoneg is disabled */ 41262306a36Sopenharmony_ci if (!hinic_is_speed_legal(nic_dev, speed)) 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci *set_settings |= HILINK_LINK_SET_SPEED; 41562306a36Sopenharmony_ci } else { 41662306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, "Need to set speed when autoneg is off\n"); 41762306a36Sopenharmony_ci return -EOPNOTSUPP; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int set_link_settings_separate_cmd(struct hinic_dev *nic_dev, 42462306a36Sopenharmony_ci u32 set_settings, u8 autoneg, 42562306a36Sopenharmony_ci u32 speed) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci enum nic_speed_level speed_level = 0; 42862306a36Sopenharmony_ci int err = 0; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (set_settings & HILINK_LINK_SET_AUTONEG) { 43162306a36Sopenharmony_ci err = hinic_set_autoneg(nic_dev->hwdev, 43262306a36Sopenharmony_ci (autoneg == AUTONEG_ENABLE)); 43362306a36Sopenharmony_ci if (err) 43462306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, "%s autoneg failed\n", 43562306a36Sopenharmony_ci (autoneg == AUTONEG_ENABLE) ? 43662306a36Sopenharmony_ci "Enable" : "Disable"); 43762306a36Sopenharmony_ci else 43862306a36Sopenharmony_ci netif_info(nic_dev, drv, nic_dev->netdev, "%s autoneg successfully\n", 43962306a36Sopenharmony_ci (autoneg == AUTONEG_ENABLE) ? 44062306a36Sopenharmony_ci "Enable" : "Disable"); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!err && (set_settings & HILINK_LINK_SET_SPEED)) { 44462306a36Sopenharmony_ci speed_level = hinic_ethtool_to_hw_speed_level(speed); 44562306a36Sopenharmony_ci err = hinic_set_speed(nic_dev->hwdev, speed_level); 44662306a36Sopenharmony_ci if (err) 44762306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, "Set speed %d failed\n", 44862306a36Sopenharmony_ci speed); 44962306a36Sopenharmony_ci else 45062306a36Sopenharmony_ci netif_info(nic_dev, drv, nic_dev->netdev, "Set speed %d successfully\n", 45162306a36Sopenharmony_ci speed); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return err; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int hinic_set_settings_to_hw(struct hinic_dev *nic_dev, 45862306a36Sopenharmony_ci u32 set_settings, u8 autoneg, u32 speed) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct hinic_link_ksettings_info settings = {0}; 46162306a36Sopenharmony_ci char set_link_str[SET_LINK_STR_MAX_LEN] = {0}; 46262306a36Sopenharmony_ci const char *autoneg_str; 46362306a36Sopenharmony_ci struct net_device *netdev = nic_dev->netdev; 46462306a36Sopenharmony_ci enum nic_speed_level speed_level = 0; 46562306a36Sopenharmony_ci int err; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci autoneg_str = (set_settings & HILINK_LINK_SET_AUTONEG) ? 46862306a36Sopenharmony_ci (autoneg ? "autong enable " : "autong disable ") : ""; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (set_settings & HILINK_LINK_SET_SPEED) { 47162306a36Sopenharmony_ci speed_level = hinic_ethtool_to_hw_speed_level(speed); 47262306a36Sopenharmony_ci err = snprintf(set_link_str, SET_LINK_STR_MAX_LEN, 47362306a36Sopenharmony_ci "speed %d ", speed); 47462306a36Sopenharmony_ci if (err >= SET_LINK_STR_MAX_LEN) { 47562306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, "Failed to snprintf link speed, function return(%d) and dest_len(%d)\n", 47662306a36Sopenharmony_ci err, SET_LINK_STR_MAX_LEN); 47762306a36Sopenharmony_ci return -EFAULT; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci settings.func_id = HINIC_HWIF_FUNC_IDX(nic_dev->hwdev->hwif); 48262306a36Sopenharmony_ci settings.valid_bitmap = set_settings; 48362306a36Sopenharmony_ci settings.autoneg = autoneg; 48462306a36Sopenharmony_ci settings.speed = speed_level; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci err = hinic_set_link_settings(nic_dev->hwdev, &settings); 48762306a36Sopenharmony_ci if (err != HINIC_MGMT_CMD_UNSUPPORTED) { 48862306a36Sopenharmony_ci if (err) 48962306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, "Set %s%sfailed\n", 49062306a36Sopenharmony_ci autoneg_str, set_link_str); 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci netif_info(nic_dev, drv, netdev, "Set %s%ssuccessfully\n", 49362306a36Sopenharmony_ci autoneg_str, set_link_str); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return err; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return set_link_settings_separate_cmd(nic_dev, set_settings, autoneg, 49962306a36Sopenharmony_ci speed); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int set_link_settings(struct net_device *netdev, u8 autoneg, u32 speed) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 50562306a36Sopenharmony_ci u32 set_settings = 0; 50662306a36Sopenharmony_ci int err; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci err = get_link_settings_type(nic_dev, autoneg, speed, &set_settings); 50962306a36Sopenharmony_ci if (err) 51062306a36Sopenharmony_ci return err; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (set_settings) 51362306a36Sopenharmony_ci err = hinic_set_settings_to_hw(nic_dev, set_settings, 51462306a36Sopenharmony_ci autoneg, speed); 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci netif_info(nic_dev, drv, netdev, "Nothing changed, exit without setting anything\n"); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return err; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int hinic_set_link_ksettings(struct net_device *netdev, const struct 52262306a36Sopenharmony_ci ethtool_link_ksettings *link_settings) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci /* only support to set autoneg and speed */ 52562306a36Sopenharmony_ci return set_link_settings(netdev, link_settings->base.autoneg, 52662306a36Sopenharmony_ci link_settings->base.speed); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic void hinic_get_drvinfo(struct net_device *netdev, 53062306a36Sopenharmony_ci struct ethtool_drvinfo *info) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 53362306a36Sopenharmony_ci u8 mgmt_ver[HINIC_MGMT_VERSION_MAX_LEN] = {0}; 53462306a36Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 53562306a36Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 53662306a36Sopenharmony_ci int err; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci strscpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); 53962306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(hwif->pdev), sizeof(info->bus_info)); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci err = hinic_get_mgmt_version(nic_dev, mgmt_ver); 54262306a36Sopenharmony_ci if (err) 54362306a36Sopenharmony_ci return; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void hinic_get_ringparam(struct net_device *netdev, 54962306a36Sopenharmony_ci struct ethtool_ringparam *ring, 55062306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 55162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci ring->rx_max_pending = HINIC_MAX_QUEUE_DEPTH; 55662306a36Sopenharmony_ci ring->tx_max_pending = HINIC_MAX_QUEUE_DEPTH; 55762306a36Sopenharmony_ci ring->rx_pending = nic_dev->rq_depth; 55862306a36Sopenharmony_ci ring->tx_pending = nic_dev->sq_depth; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int check_ringparam_valid(struct hinic_dev *nic_dev, 56262306a36Sopenharmony_ci struct ethtool_ringparam *ring) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci if (ring->rx_jumbo_pending || ring->rx_mini_pending) { 56562306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, 56662306a36Sopenharmony_ci "Unsupported rx_jumbo_pending/rx_mini_pending\n"); 56762306a36Sopenharmony_ci return -EINVAL; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (ring->tx_pending > HINIC_MAX_QUEUE_DEPTH || 57162306a36Sopenharmony_ci ring->tx_pending < HINIC_MIN_QUEUE_DEPTH || 57262306a36Sopenharmony_ci ring->rx_pending > HINIC_MAX_QUEUE_DEPTH || 57362306a36Sopenharmony_ci ring->rx_pending < HINIC_MIN_QUEUE_DEPTH) { 57462306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, 57562306a36Sopenharmony_ci "Queue depth out of range [%d-%d]\n", 57662306a36Sopenharmony_ci HINIC_MIN_QUEUE_DEPTH, HINIC_MAX_QUEUE_DEPTH); 57762306a36Sopenharmony_ci return -EINVAL; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return 0; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int hinic_set_ringparam(struct net_device *netdev, 58462306a36Sopenharmony_ci struct ethtool_ringparam *ring, 58562306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 58662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 58962306a36Sopenharmony_ci u16 new_sq_depth, new_rq_depth; 59062306a36Sopenharmony_ci int err; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci err = check_ringparam_valid(nic_dev, ring); 59362306a36Sopenharmony_ci if (err) 59462306a36Sopenharmony_ci return err; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci new_sq_depth = (u16)(1U << (u16)ilog2(ring->tx_pending)); 59762306a36Sopenharmony_ci new_rq_depth = (u16)(1U << (u16)ilog2(ring->rx_pending)); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (new_sq_depth == nic_dev->sq_depth && 60062306a36Sopenharmony_ci new_rq_depth == nic_dev->rq_depth) 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci netif_info(nic_dev, drv, netdev, 60462306a36Sopenharmony_ci "Change Tx/Rx ring depth from %d/%d to %d/%d\n", 60562306a36Sopenharmony_ci nic_dev->sq_depth, nic_dev->rq_depth, 60662306a36Sopenharmony_ci new_sq_depth, new_rq_depth); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci nic_dev->sq_depth = new_sq_depth; 60962306a36Sopenharmony_ci nic_dev->rq_depth = new_rq_depth; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (netif_running(netdev)) { 61262306a36Sopenharmony_ci netif_info(nic_dev, drv, netdev, "Restarting netdev\n"); 61362306a36Sopenharmony_ci err = hinic_close(netdev); 61462306a36Sopenharmony_ci if (err) { 61562306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 61662306a36Sopenharmony_ci "Failed to close netdev\n"); 61762306a36Sopenharmony_ci return -EFAULT; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci err = hinic_open(netdev); 62162306a36Sopenharmony_ci if (err) { 62262306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 62362306a36Sopenharmony_ci "Failed to open netdev\n"); 62462306a36Sopenharmony_ci return -EFAULT; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic int __hinic_get_coalesce(struct net_device *netdev, 63262306a36Sopenharmony_ci struct ethtool_coalesce *coal, u16 queue) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 63562306a36Sopenharmony_ci struct hinic_intr_coal_info *rx_intr_coal_info; 63662306a36Sopenharmony_ci struct hinic_intr_coal_info *tx_intr_coal_info; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (queue == COALESCE_ALL_QUEUE) { 63962306a36Sopenharmony_ci /* get tx/rx irq0 as default parameters */ 64062306a36Sopenharmony_ci rx_intr_coal_info = &nic_dev->rx_intr_coalesce[0]; 64162306a36Sopenharmony_ci tx_intr_coal_info = &nic_dev->tx_intr_coalesce[0]; 64262306a36Sopenharmony_ci } else { 64362306a36Sopenharmony_ci if (queue >= nic_dev->num_qps) { 64462306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 64562306a36Sopenharmony_ci "Invalid queue_id: %d\n", queue); 64662306a36Sopenharmony_ci return -EINVAL; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci rx_intr_coal_info = &nic_dev->rx_intr_coalesce[queue]; 64962306a36Sopenharmony_ci tx_intr_coal_info = &nic_dev->tx_intr_coalesce[queue]; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* coalesce_timer is in unit of 9us */ 65362306a36Sopenharmony_ci coal->rx_coalesce_usecs = rx_intr_coal_info->coalesce_timer_cfg * 65462306a36Sopenharmony_ci COALESCE_TIMER_CFG_UNIT; 65562306a36Sopenharmony_ci /* coalesced_frames is in unit of 8 */ 65662306a36Sopenharmony_ci coal->rx_max_coalesced_frames = rx_intr_coal_info->pending_limt * 65762306a36Sopenharmony_ci COALESCE_PENDING_LIMIT_UNIT; 65862306a36Sopenharmony_ci coal->tx_coalesce_usecs = tx_intr_coal_info->coalesce_timer_cfg * 65962306a36Sopenharmony_ci COALESCE_TIMER_CFG_UNIT; 66062306a36Sopenharmony_ci coal->tx_max_coalesced_frames = tx_intr_coal_info->pending_limt * 66162306a36Sopenharmony_ci COALESCE_PENDING_LIMIT_UNIT; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int is_coalesce_exceed_limit(const struct ethtool_coalesce *coal) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci if (coal->rx_coalesce_usecs > COALESCE_MAX_TIMER_CFG || 66962306a36Sopenharmony_ci coal->rx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT || 67062306a36Sopenharmony_ci coal->tx_coalesce_usecs > COALESCE_MAX_TIMER_CFG || 67162306a36Sopenharmony_ci coal->tx_max_coalesced_frames > COALESCE_MAX_PENDING_LIMIT) 67262306a36Sopenharmony_ci return -ERANGE; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic int set_queue_coalesce(struct hinic_dev *nic_dev, u16 q_id, 67862306a36Sopenharmony_ci struct hinic_intr_coal_info *coal, 67962306a36Sopenharmony_ci bool set_rx_coal) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct hinic_intr_coal_info *intr_coal = NULL; 68262306a36Sopenharmony_ci struct hinic_msix_config interrupt_info = {0}; 68362306a36Sopenharmony_ci struct net_device *netdev = nic_dev->netdev; 68462306a36Sopenharmony_ci u16 msix_idx; 68562306a36Sopenharmony_ci int err; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci intr_coal = set_rx_coal ? &nic_dev->rx_intr_coalesce[q_id] : 68862306a36Sopenharmony_ci &nic_dev->tx_intr_coalesce[q_id]; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci intr_coal->coalesce_timer_cfg = coal->coalesce_timer_cfg; 69162306a36Sopenharmony_ci intr_coal->pending_limt = coal->pending_limt; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* netdev not running or qp not in using, 69462306a36Sopenharmony_ci * don't need to set coalesce to hw 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_ci if (!(nic_dev->flags & HINIC_INTF_UP) || 69762306a36Sopenharmony_ci q_id >= nic_dev->num_qps) 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci msix_idx = set_rx_coal ? nic_dev->rxqs[q_id].rq->msix_entry : 70162306a36Sopenharmony_ci nic_dev->txqs[q_id].sq->msix_entry; 70262306a36Sopenharmony_ci interrupt_info.msix_index = msix_idx; 70362306a36Sopenharmony_ci interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; 70462306a36Sopenharmony_ci interrupt_info.pending_cnt = intr_coal->pending_limt; 70562306a36Sopenharmony_ci interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci err = hinic_set_interrupt_cfg(nic_dev->hwdev, &interrupt_info); 70862306a36Sopenharmony_ci if (err) 70962306a36Sopenharmony_ci netif_warn(nic_dev, drv, netdev, 71062306a36Sopenharmony_ci "Failed to set %s queue%d coalesce", 71162306a36Sopenharmony_ci set_rx_coal ? "rx" : "tx", q_id); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return err; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic int __set_hw_coal_param(struct hinic_dev *nic_dev, 71762306a36Sopenharmony_ci struct hinic_intr_coal_info *intr_coal, 71862306a36Sopenharmony_ci u16 queue, bool set_rx_coal) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci int err; 72162306a36Sopenharmony_ci u16 i; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (queue == COALESCE_ALL_QUEUE) { 72462306a36Sopenharmony_ci for (i = 0; i < nic_dev->max_qps; i++) { 72562306a36Sopenharmony_ci err = set_queue_coalesce(nic_dev, i, intr_coal, 72662306a36Sopenharmony_ci set_rx_coal); 72762306a36Sopenharmony_ci if (err) 72862306a36Sopenharmony_ci return err; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci } else { 73162306a36Sopenharmony_ci if (queue >= nic_dev->num_qps) { 73262306a36Sopenharmony_ci netif_err(nic_dev, drv, nic_dev->netdev, 73362306a36Sopenharmony_ci "Invalid queue_id: %d\n", queue); 73462306a36Sopenharmony_ci return -EINVAL; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci err = set_queue_coalesce(nic_dev, queue, intr_coal, 73762306a36Sopenharmony_ci set_rx_coal); 73862306a36Sopenharmony_ci if (err) 73962306a36Sopenharmony_ci return err; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int __hinic_set_coalesce(struct net_device *netdev, 74662306a36Sopenharmony_ci struct ethtool_coalesce *coal, u16 queue) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 74962306a36Sopenharmony_ci struct hinic_intr_coal_info rx_intr_coal = {0}; 75062306a36Sopenharmony_ci struct hinic_intr_coal_info tx_intr_coal = {0}; 75162306a36Sopenharmony_ci bool set_rx_coal = false; 75262306a36Sopenharmony_ci bool set_tx_coal = false; 75362306a36Sopenharmony_ci int err; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci err = is_coalesce_exceed_limit(coal); 75662306a36Sopenharmony_ci if (err) 75762306a36Sopenharmony_ci return err; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (coal->rx_coalesce_usecs || coal->rx_max_coalesced_frames) { 76062306a36Sopenharmony_ci rx_intr_coal.coalesce_timer_cfg = 76162306a36Sopenharmony_ci (u8)(coal->rx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT); 76262306a36Sopenharmony_ci rx_intr_coal.pending_limt = (u8)(coal->rx_max_coalesced_frames / 76362306a36Sopenharmony_ci COALESCE_PENDING_LIMIT_UNIT); 76462306a36Sopenharmony_ci set_rx_coal = true; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (coal->tx_coalesce_usecs || coal->tx_max_coalesced_frames) { 76862306a36Sopenharmony_ci tx_intr_coal.coalesce_timer_cfg = 76962306a36Sopenharmony_ci (u8)(coal->tx_coalesce_usecs / COALESCE_TIMER_CFG_UNIT); 77062306a36Sopenharmony_ci tx_intr_coal.pending_limt = (u8)(coal->tx_max_coalesced_frames / 77162306a36Sopenharmony_ci COALESCE_PENDING_LIMIT_UNIT); 77262306a36Sopenharmony_ci set_tx_coal = true; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* setting coalesce timer or pending limit to zero will disable 77662306a36Sopenharmony_ci * coalesce 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ci if (set_rx_coal && (!rx_intr_coal.coalesce_timer_cfg || 77962306a36Sopenharmony_ci !rx_intr_coal.pending_limt)) 78062306a36Sopenharmony_ci netif_warn(nic_dev, drv, netdev, "RX coalesce will be disabled\n"); 78162306a36Sopenharmony_ci if (set_tx_coal && (!tx_intr_coal.coalesce_timer_cfg || 78262306a36Sopenharmony_ci !tx_intr_coal.pending_limt)) 78362306a36Sopenharmony_ci netif_warn(nic_dev, drv, netdev, "TX coalesce will be disabled\n"); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (set_rx_coal) { 78662306a36Sopenharmony_ci err = __set_hw_coal_param(nic_dev, &rx_intr_coal, queue, true); 78762306a36Sopenharmony_ci if (err) 78862306a36Sopenharmony_ci return err; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci if (set_tx_coal) { 79162306a36Sopenharmony_ci err = __set_hw_coal_param(nic_dev, &tx_intr_coal, queue, false); 79262306a36Sopenharmony_ci if (err) 79362306a36Sopenharmony_ci return err; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci return 0; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic int hinic_get_coalesce(struct net_device *netdev, 79962306a36Sopenharmony_ci struct ethtool_coalesce *coal, 80062306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 80162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int hinic_set_coalesce(struct net_device *netdev, 80762306a36Sopenharmony_ci struct ethtool_coalesce *coal, 80862306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 80962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE); 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int hinic_get_per_queue_coalesce(struct net_device *netdev, u32 queue, 81562306a36Sopenharmony_ci struct ethtool_coalesce *coal) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci return __hinic_get_coalesce(netdev, coal, queue); 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int hinic_set_per_queue_coalesce(struct net_device *netdev, u32 queue, 82162306a36Sopenharmony_ci struct ethtool_coalesce *coal) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci return __hinic_set_coalesce(netdev, coal, queue); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void hinic_get_pauseparam(struct net_device *netdev, 82762306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 83062306a36Sopenharmony_ci struct hinic_pause_config pause_info = {0}; 83162306a36Sopenharmony_ci struct hinic_nic_cfg *nic_cfg; 83262306a36Sopenharmony_ci int err; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci nic_cfg = &nic_dev->hwdev->func_to_io.nic_cfg; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci err = hinic_get_hw_pause_info(nic_dev->hwdev, &pause_info); 83762306a36Sopenharmony_ci if (!err) { 83862306a36Sopenharmony_ci pause->autoneg = pause_info.auto_neg; 83962306a36Sopenharmony_ci if (nic_cfg->pause_set || !pause_info.auto_neg) { 84062306a36Sopenharmony_ci pause->rx_pause = nic_cfg->rx_pause; 84162306a36Sopenharmony_ci pause->tx_pause = nic_cfg->tx_pause; 84262306a36Sopenharmony_ci } else { 84362306a36Sopenharmony_ci pause->rx_pause = pause_info.rx_pause; 84462306a36Sopenharmony_ci pause->tx_pause = pause_info.tx_pause; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int hinic_set_pauseparam(struct net_device *netdev, 85062306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 85362306a36Sopenharmony_ci struct hinic_pause_config pause_info = {0}; 85462306a36Sopenharmony_ci struct hinic_port_cap port_cap = {0}; 85562306a36Sopenharmony_ci int err; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci err = hinic_port_get_cap(nic_dev, &port_cap); 85862306a36Sopenharmony_ci if (err) 85962306a36Sopenharmony_ci return -EIO; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (pause->autoneg != port_cap.autoneg_state) 86262306a36Sopenharmony_ci return -EOPNOTSUPP; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci pause_info.auto_neg = pause->autoneg; 86562306a36Sopenharmony_ci pause_info.rx_pause = pause->rx_pause; 86662306a36Sopenharmony_ci pause_info.tx_pause = pause->tx_pause; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci mutex_lock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex); 86962306a36Sopenharmony_ci err = hinic_set_hw_pause_info(nic_dev->hwdev, &pause_info); 87062306a36Sopenharmony_ci if (err) { 87162306a36Sopenharmony_ci mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex); 87262306a36Sopenharmony_ci return err; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci nic_dev->hwdev->func_to_io.nic_cfg.pause_set = true; 87562306a36Sopenharmony_ci nic_dev->hwdev->func_to_io.nic_cfg.auto_neg = pause->autoneg; 87662306a36Sopenharmony_ci nic_dev->hwdev->func_to_io.nic_cfg.rx_pause = pause->rx_pause; 87762306a36Sopenharmony_ci nic_dev->hwdev->func_to_io.nic_cfg.tx_pause = pause->tx_pause; 87862306a36Sopenharmony_ci mutex_unlock(&nic_dev->hwdev->func_to_io.nic_cfg.cfg_mutex); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci return 0; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void hinic_get_channels(struct net_device *netdev, 88462306a36Sopenharmony_ci struct ethtool_channels *channels) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 88762306a36Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci channels->max_combined = nic_dev->max_qps; 89062306a36Sopenharmony_ci channels->combined_count = hinic_hwdev_num_qps(hwdev); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int hinic_set_channels(struct net_device *netdev, 89462306a36Sopenharmony_ci struct ethtool_channels *channels) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 89762306a36Sopenharmony_ci unsigned int count = channels->combined_count; 89862306a36Sopenharmony_ci int err; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci netif_info(nic_dev, drv, netdev, "Set max combined queue number from %d to %d\n", 90162306a36Sopenharmony_ci hinic_hwdev_num_qps(nic_dev->hwdev), count); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (netif_running(netdev)) { 90462306a36Sopenharmony_ci netif_info(nic_dev, drv, netdev, "Restarting netdev\n"); 90562306a36Sopenharmony_ci hinic_close(netdev); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci nic_dev->hwdev->nic_cap.num_qps = count; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci err = hinic_open(netdev); 91062306a36Sopenharmony_ci if (err) { 91162306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 91262306a36Sopenharmony_ci "Failed to open netdev\n"); 91362306a36Sopenharmony_ci return -EFAULT; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci } else { 91662306a36Sopenharmony_ci nic_dev->hwdev->nic_cap.num_qps = count; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int hinic_get_rss_hash_opts(struct hinic_dev *nic_dev, 92362306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct hinic_rss_type rss_type = { 0 }; 92662306a36Sopenharmony_ci int err; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci cmd->data = 0; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (!(nic_dev->flags & HINIC_RSS_ENABLE)) 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci err = hinic_get_rss_type(nic_dev, nic_dev->rss_tmpl_idx, 93462306a36Sopenharmony_ci &rss_type); 93562306a36Sopenharmony_ci if (err) 93662306a36Sopenharmony_ci return err; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci cmd->data = RXH_IP_SRC | RXH_IP_DST; 93962306a36Sopenharmony_ci switch (cmd->flow_type) { 94062306a36Sopenharmony_ci case TCP_V4_FLOW: 94162306a36Sopenharmony_ci if (rss_type.tcp_ipv4) 94262306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci case TCP_V6_FLOW: 94562306a36Sopenharmony_ci if (rss_type.tcp_ipv6) 94662306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 94762306a36Sopenharmony_ci break; 94862306a36Sopenharmony_ci case UDP_V4_FLOW: 94962306a36Sopenharmony_ci if (rss_type.udp_ipv4) 95062306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci case UDP_V6_FLOW: 95362306a36Sopenharmony_ci if (rss_type.udp_ipv6) 95462306a36Sopenharmony_ci cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; 95562306a36Sopenharmony_ci break; 95662306a36Sopenharmony_ci case IPV4_FLOW: 95762306a36Sopenharmony_ci case IPV6_FLOW: 95862306a36Sopenharmony_ci break; 95962306a36Sopenharmony_ci default: 96062306a36Sopenharmony_ci cmd->data = 0; 96162306a36Sopenharmony_ci return -EINVAL; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return 0; 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic int set_l4_rss_hash_ops(struct ethtool_rxnfc *cmd, 96862306a36Sopenharmony_ci struct hinic_rss_type *rss_type) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci u8 rss_l4_en = 0; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci switch (cmd->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { 97362306a36Sopenharmony_ci case 0: 97462306a36Sopenharmony_ci rss_l4_en = 0; 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci case (RXH_L4_B_0_1 | RXH_L4_B_2_3): 97762306a36Sopenharmony_ci rss_l4_en = 1; 97862306a36Sopenharmony_ci break; 97962306a36Sopenharmony_ci default: 98062306a36Sopenharmony_ci return -EINVAL; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci switch (cmd->flow_type) { 98462306a36Sopenharmony_ci case TCP_V4_FLOW: 98562306a36Sopenharmony_ci rss_type->tcp_ipv4 = rss_l4_en; 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci case TCP_V6_FLOW: 98862306a36Sopenharmony_ci rss_type->tcp_ipv6 = rss_l4_en; 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci case UDP_V4_FLOW: 99162306a36Sopenharmony_ci rss_type->udp_ipv4 = rss_l4_en; 99262306a36Sopenharmony_ci break; 99362306a36Sopenharmony_ci case UDP_V6_FLOW: 99462306a36Sopenharmony_ci rss_type->udp_ipv6 = rss_l4_en; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci default: 99762306a36Sopenharmony_ci return -EINVAL; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int hinic_set_rss_hash_opts(struct hinic_dev *nic_dev, 100462306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct hinic_rss_type *rss_type = &nic_dev->rss_type; 100762306a36Sopenharmony_ci int err; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (!(nic_dev->flags & HINIC_RSS_ENABLE)) { 101062306a36Sopenharmony_ci cmd->data = 0; 101162306a36Sopenharmony_ci return -EOPNOTSUPP; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* RSS does not support anything other than hashing 101562306a36Sopenharmony_ci * to queues on src and dst IPs and ports 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_ci if (cmd->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | 101862306a36Sopenharmony_ci RXH_L4_B_2_3)) 101962306a36Sopenharmony_ci return -EINVAL; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* We need at least the IP SRC and DEST fields for hashing */ 102262306a36Sopenharmony_ci if (!(cmd->data & RXH_IP_SRC) || !(cmd->data & RXH_IP_DST)) 102362306a36Sopenharmony_ci return -EINVAL; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci err = hinic_get_rss_type(nic_dev, 102662306a36Sopenharmony_ci nic_dev->rss_tmpl_idx, rss_type); 102762306a36Sopenharmony_ci if (err) 102862306a36Sopenharmony_ci return -EFAULT; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci switch (cmd->flow_type) { 103162306a36Sopenharmony_ci case TCP_V4_FLOW: 103262306a36Sopenharmony_ci case TCP_V6_FLOW: 103362306a36Sopenharmony_ci case UDP_V4_FLOW: 103462306a36Sopenharmony_ci case UDP_V6_FLOW: 103562306a36Sopenharmony_ci err = set_l4_rss_hash_ops(cmd, rss_type); 103662306a36Sopenharmony_ci if (err) 103762306a36Sopenharmony_ci return err; 103862306a36Sopenharmony_ci break; 103962306a36Sopenharmony_ci case IPV4_FLOW: 104062306a36Sopenharmony_ci rss_type->ipv4 = 1; 104162306a36Sopenharmony_ci break; 104262306a36Sopenharmony_ci case IPV6_FLOW: 104362306a36Sopenharmony_ci rss_type->ipv6 = 1; 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci default: 104662306a36Sopenharmony_ci return -EINVAL; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci err = hinic_set_rss_type(nic_dev, nic_dev->rss_tmpl_idx, 105062306a36Sopenharmony_ci *rss_type); 105162306a36Sopenharmony_ci if (err) 105262306a36Sopenharmony_ci return -EFAULT; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci return 0; 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic int __set_rss_rxfh(struct net_device *netdev, 105862306a36Sopenharmony_ci const u32 *indir, const u8 *key) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 106162306a36Sopenharmony_ci int err; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (indir) { 106462306a36Sopenharmony_ci if (!nic_dev->rss_indir_user) { 106562306a36Sopenharmony_ci nic_dev->rss_indir_user = 106662306a36Sopenharmony_ci kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, 106762306a36Sopenharmony_ci GFP_KERNEL); 106862306a36Sopenharmony_ci if (!nic_dev->rss_indir_user) 106962306a36Sopenharmony_ci return -ENOMEM; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci memcpy(nic_dev->rss_indir_user, indir, 107362306a36Sopenharmony_ci sizeof(u32) * HINIC_RSS_INDIR_SIZE); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci err = hinic_rss_set_indir_tbl(nic_dev, 107662306a36Sopenharmony_ci nic_dev->rss_tmpl_idx, indir); 107762306a36Sopenharmony_ci if (err) 107862306a36Sopenharmony_ci return -EFAULT; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (key) { 108262306a36Sopenharmony_ci if (!nic_dev->rss_hkey_user) { 108362306a36Sopenharmony_ci nic_dev->rss_hkey_user = 108462306a36Sopenharmony_ci kzalloc(HINIC_RSS_KEY_SIZE * 2, GFP_KERNEL); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (!nic_dev->rss_hkey_user) 108762306a36Sopenharmony_ci return -ENOMEM; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci memcpy(nic_dev->rss_hkey_user, key, HINIC_RSS_KEY_SIZE); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci err = hinic_rss_set_template_tbl(nic_dev, 109362306a36Sopenharmony_ci nic_dev->rss_tmpl_idx, key); 109462306a36Sopenharmony_ci if (err) 109562306a36Sopenharmony_ci return -EFAULT; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int hinic_get_rxnfc(struct net_device *netdev, 110262306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, u32 *rule_locs) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 110562306a36Sopenharmony_ci int err = 0; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci switch (cmd->cmd) { 110862306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 110962306a36Sopenharmony_ci cmd->data = nic_dev->num_qps; 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci case ETHTOOL_GRXFH: 111262306a36Sopenharmony_ci err = hinic_get_rss_hash_opts(nic_dev, cmd); 111362306a36Sopenharmony_ci break; 111462306a36Sopenharmony_ci default: 111562306a36Sopenharmony_ci err = -EOPNOTSUPP; 111662306a36Sopenharmony_ci break; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci return err; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic int hinic_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 112562306a36Sopenharmony_ci int err = 0; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci switch (cmd->cmd) { 112862306a36Sopenharmony_ci case ETHTOOL_SRXFH: 112962306a36Sopenharmony_ci err = hinic_set_rss_hash_opts(nic_dev, cmd); 113062306a36Sopenharmony_ci break; 113162306a36Sopenharmony_ci default: 113262306a36Sopenharmony_ci err = -EOPNOTSUPP; 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci return err; 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic int hinic_get_rxfh(struct net_device *netdev, 114062306a36Sopenharmony_ci u32 *indir, u8 *key, u8 *hfunc) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 114362306a36Sopenharmony_ci u8 hash_engine_type = 0; 114462306a36Sopenharmony_ci int err = 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (!(nic_dev->flags & HINIC_RSS_ENABLE)) 114762306a36Sopenharmony_ci return -EOPNOTSUPP; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (hfunc) { 115062306a36Sopenharmony_ci err = hinic_rss_get_hash_engine(nic_dev, 115162306a36Sopenharmony_ci nic_dev->rss_tmpl_idx, 115262306a36Sopenharmony_ci &hash_engine_type); 115362306a36Sopenharmony_ci if (err) 115462306a36Sopenharmony_ci return -EFAULT; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci *hfunc = hash_engine_type ? ETH_RSS_HASH_TOP : ETH_RSS_HASH_XOR; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (indir) { 116062306a36Sopenharmony_ci err = hinic_rss_get_indir_tbl(nic_dev, 116162306a36Sopenharmony_ci nic_dev->rss_tmpl_idx, indir); 116262306a36Sopenharmony_ci if (err) 116362306a36Sopenharmony_ci return -EFAULT; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (key) 116762306a36Sopenharmony_ci err = hinic_rss_get_template_tbl(nic_dev, 116862306a36Sopenharmony_ci nic_dev->rss_tmpl_idx, key); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci return err; 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic int hinic_set_rxfh(struct net_device *netdev, const u32 *indir, 117462306a36Sopenharmony_ci const u8 *key, const u8 hfunc) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 117762306a36Sopenharmony_ci int err = 0; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (!(nic_dev->flags & HINIC_RSS_ENABLE)) 118062306a36Sopenharmony_ci return -EOPNOTSUPP; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE) { 118362306a36Sopenharmony_ci if (hfunc != ETH_RSS_HASH_TOP && hfunc != ETH_RSS_HASH_XOR) 118462306a36Sopenharmony_ci return -EOPNOTSUPP; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci nic_dev->rss_hash_engine = (hfunc == ETH_RSS_HASH_XOR) ? 118762306a36Sopenharmony_ci HINIC_RSS_HASH_ENGINE_TYPE_XOR : 118862306a36Sopenharmony_ci HINIC_RSS_HASH_ENGINE_TYPE_TOEP; 118962306a36Sopenharmony_ci err = hinic_rss_set_hash_engine 119062306a36Sopenharmony_ci (nic_dev, nic_dev->rss_tmpl_idx, 119162306a36Sopenharmony_ci nic_dev->rss_hash_engine); 119262306a36Sopenharmony_ci if (err) 119362306a36Sopenharmony_ci return -EFAULT; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci err = __set_rss_rxfh(netdev, indir, key); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci return err; 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic u32 hinic_get_rxfh_key_size(struct net_device *netdev) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci return HINIC_RSS_KEY_SIZE; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic u32 hinic_get_rxfh_indir_size(struct net_device *netdev) 120762306a36Sopenharmony_ci{ 120862306a36Sopenharmony_ci return HINIC_RSS_INDIR_SIZE; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci#define HINIC_FUNC_STAT(_stat_item) { \ 121262306a36Sopenharmony_ci .name = #_stat_item, \ 121362306a36Sopenharmony_ci .size = sizeof_field(struct hinic_vport_stats, _stat_item), \ 121462306a36Sopenharmony_ci .offset = offsetof(struct hinic_vport_stats, _stat_item) \ 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistatic struct hinic_stats hinic_function_stats[] = { 121862306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_unicast_pkts_vport), 121962306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_unicast_bytes_vport), 122062306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_multicast_pkts_vport), 122162306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_multicast_bytes_vport), 122262306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_broadcast_pkts_vport), 122362306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_broadcast_bytes_vport), 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_unicast_pkts_vport), 122662306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_unicast_bytes_vport), 122762306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_multicast_pkts_vport), 122862306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_multicast_bytes_vport), 122962306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_broadcast_pkts_vport), 123062306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_broadcast_bytes_vport), 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_discard_vport), 123362306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_discard_vport), 123462306a36Sopenharmony_ci HINIC_FUNC_STAT(tx_err_vport), 123562306a36Sopenharmony_ci HINIC_FUNC_STAT(rx_err_vport), 123662306a36Sopenharmony_ci}; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic char hinic_test_strings[][ETH_GSTRING_LEN] = { 123962306a36Sopenharmony_ci "Internal lb test (on/offline)", 124062306a36Sopenharmony_ci "External lb test (external_lb)", 124162306a36Sopenharmony_ci}; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci#define HINIC_PORT_STAT(_stat_item) { \ 124462306a36Sopenharmony_ci .name = #_stat_item, \ 124562306a36Sopenharmony_ci .size = sizeof_field(struct hinic_phy_port_stats, _stat_item), \ 124662306a36Sopenharmony_ci .offset = offsetof(struct hinic_phy_port_stats, _stat_item) \ 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic struct hinic_stats hinic_port_stats[] = { 125062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_total_pkt_num), 125162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_total_oct_num), 125262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_bad_pkt_num), 125362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_bad_oct_num), 125462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_good_pkt_num), 125562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_good_oct_num), 125662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_uni_pkt_num), 125762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_multi_pkt_num), 125862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_broad_pkt_num), 125962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_total_pkt_num), 126062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_total_oct_num), 126162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_bad_pkt_num), 126262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_bad_oct_num), 126362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_good_pkt_num), 126462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_good_oct_num), 126562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_uni_pkt_num), 126662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_multi_pkt_num), 126762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_broad_pkt_num), 126862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_fragment_pkt_num), 126962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_undersize_pkt_num), 127062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_undermin_pkt_num), 127162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_64_oct_pkt_num), 127262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_65_127_oct_pkt_num), 127362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_128_255_oct_pkt_num), 127462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_256_511_oct_pkt_num), 127562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_512_1023_oct_pkt_num), 127662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_1024_1518_oct_pkt_num), 127762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_1519_2047_oct_pkt_num), 127862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_2048_4095_oct_pkt_num), 127962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_4096_8191_oct_pkt_num), 128062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_8192_9216_oct_pkt_num), 128162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_9217_12287_oct_pkt_num), 128262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_12288_16383_oct_pkt_num), 128362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_1519_max_good_pkt_num), 128462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_1519_max_bad_pkt_num), 128562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_oversize_pkt_num), 128662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_jabber_pkt_num), 128762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pause_num), 128862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pkt_num), 128962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri0_pkt_num), 129062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri1_pkt_num), 129162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri2_pkt_num), 129262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri3_pkt_num), 129362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri4_pkt_num), 129462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri5_pkt_num), 129562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri6_pkt_num), 129662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_pfc_pri7_pkt_num), 129762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_control_pkt_num), 129862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_sym_err_pkt_num), 129962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_fcs_err_pkt_num), 130062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_send_app_good_pkt_num), 130162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_rx_send_app_bad_pkt_num), 130262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_fragment_pkt_num), 130362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_undersize_pkt_num), 130462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_undermin_pkt_num), 130562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_64_oct_pkt_num), 130662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_65_127_oct_pkt_num), 130762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_128_255_oct_pkt_num), 130862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_256_511_oct_pkt_num), 130962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_512_1023_oct_pkt_num), 131062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_1024_1518_oct_pkt_num), 131162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_1519_2047_oct_pkt_num), 131262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_2048_4095_oct_pkt_num), 131362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_4096_8191_oct_pkt_num), 131462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_8192_9216_oct_pkt_num), 131562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_9217_12287_oct_pkt_num), 131662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_12288_16383_oct_pkt_num), 131762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_1519_max_good_pkt_num), 131862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_1519_max_bad_pkt_num), 131962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_oversize_pkt_num), 132062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_jabber_pkt_num), 132162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pause_num), 132262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pkt_num), 132362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri0_pkt_num), 132462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri1_pkt_num), 132562306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri2_pkt_num), 132662306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri3_pkt_num), 132762306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri4_pkt_num), 132862306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri5_pkt_num), 132962306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri6_pkt_num), 133062306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_pfc_pri7_pkt_num), 133162306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_control_pkt_num), 133262306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_err_all_pkt_num), 133362306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_from_app_good_pkt_num), 133462306a36Sopenharmony_ci HINIC_PORT_STAT(mac_tx_from_app_bad_pkt_num), 133562306a36Sopenharmony_ci}; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci#define HINIC_TXQ_STAT(_stat_item) { \ 133862306a36Sopenharmony_ci .name = "txq%d_"#_stat_item, \ 133962306a36Sopenharmony_ci .size = sizeof_field(struct hinic_txq_stats, _stat_item), \ 134062306a36Sopenharmony_ci .offset = offsetof(struct hinic_txq_stats, _stat_item) \ 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cistatic struct hinic_stats hinic_tx_queue_stats[] = { 134462306a36Sopenharmony_ci HINIC_TXQ_STAT(pkts), 134562306a36Sopenharmony_ci HINIC_TXQ_STAT(bytes), 134662306a36Sopenharmony_ci HINIC_TXQ_STAT(tx_busy), 134762306a36Sopenharmony_ci HINIC_TXQ_STAT(tx_wake), 134862306a36Sopenharmony_ci HINIC_TXQ_STAT(tx_dropped), 134962306a36Sopenharmony_ci HINIC_TXQ_STAT(big_frags_pkts), 135062306a36Sopenharmony_ci}; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci#define HINIC_RXQ_STAT(_stat_item) { \ 135362306a36Sopenharmony_ci .name = "rxq%d_"#_stat_item, \ 135462306a36Sopenharmony_ci .size = sizeof_field(struct hinic_rxq_stats, _stat_item), \ 135562306a36Sopenharmony_ci .offset = offsetof(struct hinic_rxq_stats, _stat_item) \ 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic struct hinic_stats hinic_rx_queue_stats[] = { 135962306a36Sopenharmony_ci HINIC_RXQ_STAT(pkts), 136062306a36Sopenharmony_ci HINIC_RXQ_STAT(bytes), 136162306a36Sopenharmony_ci HINIC_RXQ_STAT(errors), 136262306a36Sopenharmony_ci HINIC_RXQ_STAT(csum_errors), 136362306a36Sopenharmony_ci HINIC_RXQ_STAT(other_errors), 136462306a36Sopenharmony_ci}; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_cistatic void get_drv_queue_stats(struct hinic_dev *nic_dev, u64 *data) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct hinic_txq_stats txq_stats; 136962306a36Sopenharmony_ci struct hinic_rxq_stats rxq_stats; 137062306a36Sopenharmony_ci u16 i = 0, j = 0, qid = 0; 137162306a36Sopenharmony_ci char *p; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci for (qid = 0; qid < nic_dev->num_qps; qid++) { 137462306a36Sopenharmony_ci if (!nic_dev->txqs) 137562306a36Sopenharmony_ci break; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci hinic_txq_get_stats(&nic_dev->txqs[qid], &txq_stats); 137862306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(hinic_tx_queue_stats); j++, i++) { 137962306a36Sopenharmony_ci p = (char *)&txq_stats + 138062306a36Sopenharmony_ci hinic_tx_queue_stats[j].offset; 138162306a36Sopenharmony_ci data[i] = (hinic_tx_queue_stats[j].size == 138262306a36Sopenharmony_ci sizeof(u64)) ? *(u64 *)p : *(u32 *)p; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci for (qid = 0; qid < nic_dev->num_qps; qid++) { 138762306a36Sopenharmony_ci if (!nic_dev->rxqs) 138862306a36Sopenharmony_ci break; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci hinic_rxq_get_stats(&nic_dev->rxqs[qid], &rxq_stats); 139162306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(hinic_rx_queue_stats); j++, i++) { 139262306a36Sopenharmony_ci p = (char *)&rxq_stats + 139362306a36Sopenharmony_ci hinic_rx_queue_stats[j].offset; 139462306a36Sopenharmony_ci data[i] = (hinic_rx_queue_stats[j].size == 139562306a36Sopenharmony_ci sizeof(u64)) ? *(u64 *)p : *(u32 *)p; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic void hinic_get_ethtool_stats(struct net_device *netdev, 140162306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 140462306a36Sopenharmony_ci struct hinic_vport_stats vport_stats = {0}; 140562306a36Sopenharmony_ci struct hinic_phy_port_stats *port_stats; 140662306a36Sopenharmony_ci u16 i = 0, j = 0; 140762306a36Sopenharmony_ci char *p; 140862306a36Sopenharmony_ci int err; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci err = hinic_get_vport_stats(nic_dev, &vport_stats); 141162306a36Sopenharmony_ci if (err) 141262306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 141362306a36Sopenharmony_ci "Failed to get vport stats from firmware\n"); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(hinic_function_stats); j++, i++) { 141662306a36Sopenharmony_ci p = (char *)&vport_stats + hinic_function_stats[j].offset; 141762306a36Sopenharmony_ci data[i] = (hinic_function_stats[j].size == 141862306a36Sopenharmony_ci sizeof(u64)) ? *(u64 *)p : *(u32 *)p; 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); 142262306a36Sopenharmony_ci if (!port_stats) { 142362306a36Sopenharmony_ci memset(&data[i], 0, 142462306a36Sopenharmony_ci ARRAY_SIZE(hinic_port_stats) * sizeof(*data)); 142562306a36Sopenharmony_ci i += ARRAY_SIZE(hinic_port_stats); 142662306a36Sopenharmony_ci goto get_drv_stats; 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci err = hinic_get_phy_port_stats(nic_dev, port_stats); 143062306a36Sopenharmony_ci if (err) 143162306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 143262306a36Sopenharmony_ci "Failed to get port stats from firmware\n"); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(hinic_port_stats); j++, i++) { 143562306a36Sopenharmony_ci p = (char *)port_stats + hinic_port_stats[j].offset; 143662306a36Sopenharmony_ci data[i] = (hinic_port_stats[j].size == 143762306a36Sopenharmony_ci sizeof(u64)) ? *(u64 *)p : *(u32 *)p; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci kfree(port_stats); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ciget_drv_stats: 144362306a36Sopenharmony_ci get_drv_queue_stats(nic_dev, data + i); 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_cistatic int hinic_get_sset_count(struct net_device *netdev, int sset) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 144962306a36Sopenharmony_ci int count, q_num; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci switch (sset) { 145262306a36Sopenharmony_ci case ETH_SS_TEST: 145362306a36Sopenharmony_ci return ARRAY_SIZE(hinic_test_strings); 145462306a36Sopenharmony_ci case ETH_SS_STATS: 145562306a36Sopenharmony_ci q_num = nic_dev->num_qps; 145662306a36Sopenharmony_ci count = ARRAY_SIZE(hinic_function_stats) + 145762306a36Sopenharmony_ci (ARRAY_SIZE(hinic_tx_queue_stats) + 145862306a36Sopenharmony_ci ARRAY_SIZE(hinic_rx_queue_stats)) * q_num; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci count += ARRAY_SIZE(hinic_port_stats); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci return count; 146362306a36Sopenharmony_ci default: 146462306a36Sopenharmony_ci return -EOPNOTSUPP; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cistatic void hinic_get_strings(struct net_device *netdev, 146962306a36Sopenharmony_ci u32 stringset, u8 *data) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 147262306a36Sopenharmony_ci char *p = (char *)data; 147362306a36Sopenharmony_ci u16 i, j; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci switch (stringset) { 147662306a36Sopenharmony_ci case ETH_SS_TEST: 147762306a36Sopenharmony_ci memcpy(data, *hinic_test_strings, sizeof(hinic_test_strings)); 147862306a36Sopenharmony_ci return; 147962306a36Sopenharmony_ci case ETH_SS_STATS: 148062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hinic_function_stats); i++) { 148162306a36Sopenharmony_ci memcpy(p, hinic_function_stats[i].name, 148262306a36Sopenharmony_ci ETH_GSTRING_LEN); 148362306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hinic_port_stats); i++) { 148762306a36Sopenharmony_ci memcpy(p, hinic_port_stats[i].name, 148862306a36Sopenharmony_ci ETH_GSTRING_LEN); 148962306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci for (i = 0; i < nic_dev->num_qps; i++) { 149362306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(hinic_tx_queue_stats); j++) { 149462306a36Sopenharmony_ci sprintf(p, hinic_tx_queue_stats[j].name, i); 149562306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci for (i = 0; i < nic_dev->num_qps; i++) { 150062306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(hinic_rx_queue_stats); j++) { 150162306a36Sopenharmony_ci sprintf(p, hinic_rx_queue_stats[j].name, i); 150262306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return; 150762306a36Sopenharmony_ci default: 150862306a36Sopenharmony_ci return; 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci} 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_cistatic int hinic_run_lp_test(struct hinic_dev *nic_dev, u32 test_time) 151362306a36Sopenharmony_ci{ 151462306a36Sopenharmony_ci u8 *lb_test_rx_buf = nic_dev->lb_test_rx_buf; 151562306a36Sopenharmony_ci struct net_device *netdev = nic_dev->netdev; 151662306a36Sopenharmony_ci struct sk_buff *skb_tmp = NULL; 151762306a36Sopenharmony_ci struct sk_buff *skb = NULL; 151862306a36Sopenharmony_ci u32 cnt = test_time * 5; 151962306a36Sopenharmony_ci u8 *test_data = NULL; 152062306a36Sopenharmony_ci u32 i; 152162306a36Sopenharmony_ci u8 j; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci skb_tmp = alloc_skb(LP_PKT_LEN, GFP_ATOMIC); 152462306a36Sopenharmony_ci if (!skb_tmp) 152562306a36Sopenharmony_ci return -ENOMEM; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci test_data = __skb_put(skb_tmp, LP_PKT_LEN); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci memset(test_data, 0xFF, 2 * ETH_ALEN); 153062306a36Sopenharmony_ci test_data[ETH_ALEN] = 0xFE; 153162306a36Sopenharmony_ci test_data[2 * ETH_ALEN] = 0x08; 153262306a36Sopenharmony_ci test_data[2 * ETH_ALEN + 1] = 0x0; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci for (i = ETH_HLEN; i < LP_PKT_LEN; i++) 153562306a36Sopenharmony_ci test_data[i] = i & 0xFF; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci skb_tmp->queue_mapping = 0; 153862306a36Sopenharmony_ci skb_tmp->ip_summed = CHECKSUM_COMPLETE; 153962306a36Sopenharmony_ci skb_tmp->dev = netdev; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci for (i = 0; i < cnt; i++) { 154262306a36Sopenharmony_ci nic_dev->lb_test_rx_idx = 0; 154362306a36Sopenharmony_ci memset(lb_test_rx_buf, 0, LP_PKT_CNT * LP_PKT_LEN); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci for (j = 0; j < LP_PKT_CNT; j++) { 154662306a36Sopenharmony_ci skb = pskb_copy(skb_tmp, GFP_ATOMIC); 154762306a36Sopenharmony_ci if (!skb) { 154862306a36Sopenharmony_ci dev_kfree_skb_any(skb_tmp); 154962306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 155062306a36Sopenharmony_ci "Copy skb failed for loopback test\n"); 155162306a36Sopenharmony_ci return -ENOMEM; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* mark index for every pkt */ 155562306a36Sopenharmony_ci skb->data[LP_PKT_LEN - 1] = j; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (hinic_lb_xmit_frame(skb, netdev)) { 155862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 155962306a36Sopenharmony_ci dev_kfree_skb_any(skb_tmp); 156062306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 156162306a36Sopenharmony_ci "Xmit pkt failed for loopback test\n"); 156262306a36Sopenharmony_ci return -EBUSY; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* wait till all pkts received to RX buffer */ 156762306a36Sopenharmony_ci msleep(200); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci for (j = 0; j < LP_PKT_CNT; j++) { 157062306a36Sopenharmony_ci if (memcmp(lb_test_rx_buf + j * LP_PKT_LEN, 157162306a36Sopenharmony_ci skb_tmp->data, LP_PKT_LEN - 1) || 157262306a36Sopenharmony_ci (*(lb_test_rx_buf + j * LP_PKT_LEN + 157362306a36Sopenharmony_ci LP_PKT_LEN - 1) != j)) { 157462306a36Sopenharmony_ci dev_kfree_skb_any(skb_tmp); 157562306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 157662306a36Sopenharmony_ci "Compare pkt failed in loopback test(index=0x%02x, data[%d]=0x%02x)\n", 157762306a36Sopenharmony_ci j + i * LP_PKT_CNT, 157862306a36Sopenharmony_ci LP_PKT_LEN - 1, 157962306a36Sopenharmony_ci *(lb_test_rx_buf + j * LP_PKT_LEN + 158062306a36Sopenharmony_ci LP_PKT_LEN - 1)); 158162306a36Sopenharmony_ci return -EIO; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci } 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci dev_kfree_skb_any(skb_tmp); 158762306a36Sopenharmony_ci return 0; 158862306a36Sopenharmony_ci} 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_cistatic int do_lp_test(struct hinic_dev *nic_dev, u32 flags, u32 test_time, 159162306a36Sopenharmony_ci enum diag_test_index *test_index) 159262306a36Sopenharmony_ci{ 159362306a36Sopenharmony_ci struct net_device *netdev = nic_dev->netdev; 159462306a36Sopenharmony_ci u8 *lb_test_rx_buf = NULL; 159562306a36Sopenharmony_ci int err = 0; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) { 159862306a36Sopenharmony_ci *test_index = INTERNAL_LP_TEST; 159962306a36Sopenharmony_ci if (hinic_set_loopback_mode(nic_dev->hwdev, 160062306a36Sopenharmony_ci HINIC_INTERNAL_LP_MODE, true)) { 160162306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 160262306a36Sopenharmony_ci "Failed to set port loopback mode before loopback test\n"); 160362306a36Sopenharmony_ci return -EIO; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci } else { 160662306a36Sopenharmony_ci *test_index = EXTERNAL_LP_TEST; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci lb_test_rx_buf = vmalloc(LP_PKT_CNT * LP_PKT_LEN); 161062306a36Sopenharmony_ci if (!lb_test_rx_buf) { 161162306a36Sopenharmony_ci err = -ENOMEM; 161262306a36Sopenharmony_ci } else { 161362306a36Sopenharmony_ci nic_dev->lb_test_rx_buf = lb_test_rx_buf; 161462306a36Sopenharmony_ci nic_dev->lb_pkt_len = LP_PKT_LEN; 161562306a36Sopenharmony_ci nic_dev->flags |= HINIC_LP_TEST; 161662306a36Sopenharmony_ci err = hinic_run_lp_test(nic_dev, test_time); 161762306a36Sopenharmony_ci nic_dev->flags &= ~HINIC_LP_TEST; 161862306a36Sopenharmony_ci msleep(100); 161962306a36Sopenharmony_ci vfree(lb_test_rx_buf); 162062306a36Sopenharmony_ci nic_dev->lb_test_rx_buf = NULL; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci if (!(flags & ETH_TEST_FL_EXTERNAL_LB)) { 162462306a36Sopenharmony_ci if (hinic_set_loopback_mode(nic_dev->hwdev, 162562306a36Sopenharmony_ci HINIC_INTERNAL_LP_MODE, false)) { 162662306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 162762306a36Sopenharmony_ci "Failed to cancel port loopback mode after loopback test\n"); 162862306a36Sopenharmony_ci err = -EIO; 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci return err; 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_cistatic void hinic_diag_test(struct net_device *netdev, 163662306a36Sopenharmony_ci struct ethtool_test *eth_test, u64 *data) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 163962306a36Sopenharmony_ci enum hinic_port_link_state link_state; 164062306a36Sopenharmony_ci enum diag_test_index test_index = 0; 164162306a36Sopenharmony_ci int err = 0; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci memset(data, 0, DIAG_TEST_MAX * sizeof(u64)); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci /* don't support loopback test when netdev is closed. */ 164662306a36Sopenharmony_ci if (!(nic_dev->flags & HINIC_INTF_UP)) { 164762306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 164862306a36Sopenharmony_ci "Do not support loopback test when netdev is closed\n"); 164962306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 165062306a36Sopenharmony_ci data[PORT_DOWN_ERR_IDX] = 1; 165162306a36Sopenharmony_ci return; 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci netif_carrier_off(netdev); 165562306a36Sopenharmony_ci netif_tx_disable(netdev); 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci err = do_lp_test(nic_dev, eth_test->flags, LP_DEFAULT_TIME, 165862306a36Sopenharmony_ci &test_index); 165962306a36Sopenharmony_ci if (err) { 166062306a36Sopenharmony_ci eth_test->flags |= ETH_TEST_FL_FAILED; 166162306a36Sopenharmony_ci data[test_index] = 1; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci netif_tx_wake_all_queues(netdev); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci err = hinic_port_link_state(nic_dev, &link_state); 166762306a36Sopenharmony_ci if (!err && link_state == HINIC_LINK_STATE_UP) 166862306a36Sopenharmony_ci netif_carrier_on(netdev); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic int hinic_set_phys_id(struct net_device *netdev, 167262306a36Sopenharmony_ci enum ethtool_phys_id_state state) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 167562306a36Sopenharmony_ci int err = 0; 167662306a36Sopenharmony_ci u8 port; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci port = nic_dev->hwdev->port_id; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci switch (state) { 168162306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 168262306a36Sopenharmony_ci err = hinic_set_led_status(nic_dev->hwdev, port, 168362306a36Sopenharmony_ci HINIC_LED_TYPE_LINK, 168462306a36Sopenharmony_ci HINIC_LED_MODE_FORCE_2HZ); 168562306a36Sopenharmony_ci if (err) 168662306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 168762306a36Sopenharmony_ci "Set LED blinking in 2HZ failed\n"); 168862306a36Sopenharmony_ci break; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 169162306a36Sopenharmony_ci err = hinic_reset_led_status(nic_dev->hwdev, port); 169262306a36Sopenharmony_ci if (err) 169362306a36Sopenharmony_ci netif_err(nic_dev, drv, netdev, 169462306a36Sopenharmony_ci "Reset LED to original status failed\n"); 169562306a36Sopenharmony_ci break; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci default: 169862306a36Sopenharmony_ci return -EOPNOTSUPP; 169962306a36Sopenharmony_ci } 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci return err; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic int hinic_get_module_info(struct net_device *netdev, 170562306a36Sopenharmony_ci struct ethtool_modinfo *modinfo) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 170862306a36Sopenharmony_ci u8 sfp_type_ext; 170962306a36Sopenharmony_ci u8 sfp_type; 171062306a36Sopenharmony_ci int err; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci err = hinic_get_sfp_type(nic_dev->hwdev, &sfp_type, &sfp_type_ext); 171362306a36Sopenharmony_ci if (err) 171462306a36Sopenharmony_ci return err; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci switch (sfp_type) { 171762306a36Sopenharmony_ci case SFF8024_ID_SFP: 171862306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8472; 171962306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 172062306a36Sopenharmony_ci break; 172162306a36Sopenharmony_ci case SFF8024_ID_QSFP_8438: 172262306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8436; 172362306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; 172462306a36Sopenharmony_ci break; 172562306a36Sopenharmony_ci case SFF8024_ID_QSFP_8436_8636: 172662306a36Sopenharmony_ci if (sfp_type_ext >= 0x3) { 172762306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8636; 172862306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci } else { 173162306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8436; 173262306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci break; 173562306a36Sopenharmony_ci case SFF8024_ID_QSFP28_8636: 173662306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8636; 173762306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8636_MAX_LEN; 173862306a36Sopenharmony_ci break; 173962306a36Sopenharmony_ci default: 174062306a36Sopenharmony_ci netif_warn(nic_dev, drv, netdev, 174162306a36Sopenharmony_ci "Optical module unknown: 0x%x\n", sfp_type); 174262306a36Sopenharmony_ci return -EINVAL; 174362306a36Sopenharmony_ci } 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci return 0; 174662306a36Sopenharmony_ci} 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_cistatic int hinic_get_module_eeprom(struct net_device *netdev, 174962306a36Sopenharmony_ci struct ethtool_eeprom *ee, u8 *data) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 175262306a36Sopenharmony_ci u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; 175362306a36Sopenharmony_ci u16 len; 175462306a36Sopenharmony_ci int err; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci if (!ee->len || ((ee->len + ee->offset) > STD_SFP_INFO_MAX_SIZE)) 175762306a36Sopenharmony_ci return -EINVAL; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci memset(data, 0, ee->len); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci err = hinic_get_sfp_eeprom(nic_dev->hwdev, sfp_data, &len); 176262306a36Sopenharmony_ci if (err) 176362306a36Sopenharmony_ci return err; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci memcpy(data, sfp_data + ee->offset, ee->len); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci return 0; 176862306a36Sopenharmony_ci} 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_cistatic int 177162306a36Sopenharmony_cihinic_get_link_ext_state(struct net_device *netdev, 177262306a36Sopenharmony_ci struct ethtool_link_ext_state_info *link_ext_state_info) 177362306a36Sopenharmony_ci{ 177462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci if (netif_carrier_ok(netdev)) 177762306a36Sopenharmony_ci return -ENODATA; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci if (nic_dev->cable_unplugged) 178062306a36Sopenharmony_ci link_ext_state_info->link_ext_state = 178162306a36Sopenharmony_ci ETHTOOL_LINK_EXT_STATE_NO_CABLE; 178262306a36Sopenharmony_ci else if (nic_dev->module_unrecognized) 178362306a36Sopenharmony_ci link_ext_state_info->link_ext_state = 178462306a36Sopenharmony_ci ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci return 0; 178762306a36Sopenharmony_ci} 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_cistatic const struct ethtool_ops hinic_ethtool_ops = { 179062306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | 179162306a36Sopenharmony_ci ETHTOOL_COALESCE_RX_MAX_FRAMES | 179262306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_USECS | 179362306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_MAX_FRAMES, 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci .get_link_ksettings = hinic_get_link_ksettings, 179662306a36Sopenharmony_ci .set_link_ksettings = hinic_set_link_ksettings, 179762306a36Sopenharmony_ci .get_drvinfo = hinic_get_drvinfo, 179862306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 179962306a36Sopenharmony_ci .get_link_ext_state = hinic_get_link_ext_state, 180062306a36Sopenharmony_ci .get_ringparam = hinic_get_ringparam, 180162306a36Sopenharmony_ci .set_ringparam = hinic_set_ringparam, 180262306a36Sopenharmony_ci .get_coalesce = hinic_get_coalesce, 180362306a36Sopenharmony_ci .set_coalesce = hinic_set_coalesce, 180462306a36Sopenharmony_ci .get_per_queue_coalesce = hinic_get_per_queue_coalesce, 180562306a36Sopenharmony_ci .set_per_queue_coalesce = hinic_set_per_queue_coalesce, 180662306a36Sopenharmony_ci .get_pauseparam = hinic_get_pauseparam, 180762306a36Sopenharmony_ci .set_pauseparam = hinic_set_pauseparam, 180862306a36Sopenharmony_ci .get_channels = hinic_get_channels, 180962306a36Sopenharmony_ci .set_channels = hinic_set_channels, 181062306a36Sopenharmony_ci .get_rxnfc = hinic_get_rxnfc, 181162306a36Sopenharmony_ci .set_rxnfc = hinic_set_rxnfc, 181262306a36Sopenharmony_ci .get_rxfh_key_size = hinic_get_rxfh_key_size, 181362306a36Sopenharmony_ci .get_rxfh_indir_size = hinic_get_rxfh_indir_size, 181462306a36Sopenharmony_ci .get_rxfh = hinic_get_rxfh, 181562306a36Sopenharmony_ci .set_rxfh = hinic_set_rxfh, 181662306a36Sopenharmony_ci .get_sset_count = hinic_get_sset_count, 181762306a36Sopenharmony_ci .get_ethtool_stats = hinic_get_ethtool_stats, 181862306a36Sopenharmony_ci .get_strings = hinic_get_strings, 181962306a36Sopenharmony_ci .self_test = hinic_diag_test, 182062306a36Sopenharmony_ci .set_phys_id = hinic_set_phys_id, 182162306a36Sopenharmony_ci .get_module_info = hinic_get_module_info, 182262306a36Sopenharmony_ci .get_module_eeprom = hinic_get_module_eeprom, 182362306a36Sopenharmony_ci}; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_cistatic const struct ethtool_ops hinicvf_ethtool_ops = { 182662306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | 182762306a36Sopenharmony_ci ETHTOOL_COALESCE_RX_MAX_FRAMES | 182862306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_USECS | 182962306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_MAX_FRAMES, 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci .get_link_ksettings = hinic_get_link_ksettings, 183262306a36Sopenharmony_ci .get_drvinfo = hinic_get_drvinfo, 183362306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 183462306a36Sopenharmony_ci .get_ringparam = hinic_get_ringparam, 183562306a36Sopenharmony_ci .set_ringparam = hinic_set_ringparam, 183662306a36Sopenharmony_ci .get_coalesce = hinic_get_coalesce, 183762306a36Sopenharmony_ci .set_coalesce = hinic_set_coalesce, 183862306a36Sopenharmony_ci .get_per_queue_coalesce = hinic_get_per_queue_coalesce, 183962306a36Sopenharmony_ci .set_per_queue_coalesce = hinic_set_per_queue_coalesce, 184062306a36Sopenharmony_ci .get_channels = hinic_get_channels, 184162306a36Sopenharmony_ci .set_channels = hinic_set_channels, 184262306a36Sopenharmony_ci .get_rxnfc = hinic_get_rxnfc, 184362306a36Sopenharmony_ci .set_rxnfc = hinic_set_rxnfc, 184462306a36Sopenharmony_ci .get_rxfh_key_size = hinic_get_rxfh_key_size, 184562306a36Sopenharmony_ci .get_rxfh_indir_size = hinic_get_rxfh_indir_size, 184662306a36Sopenharmony_ci .get_rxfh = hinic_get_rxfh, 184762306a36Sopenharmony_ci .set_rxfh = hinic_set_rxfh, 184862306a36Sopenharmony_ci .get_sset_count = hinic_get_sset_count, 184962306a36Sopenharmony_ci .get_ethtool_stats = hinic_get_ethtool_stats, 185062306a36Sopenharmony_ci .get_strings = hinic_get_strings, 185162306a36Sopenharmony_ci}; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_civoid hinic_set_ethtool_ops(struct net_device *netdev) 185462306a36Sopenharmony_ci{ 185562306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 185862306a36Sopenharmony_ci netdev->ethtool_ops = &hinic_ethtool_ops; 185962306a36Sopenharmony_ci else 186062306a36Sopenharmony_ci netdev->ethtool_ops = &hinicvf_ethtool_ops; 186162306a36Sopenharmony_ci} 1862