18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 68c2ecf20Sopenharmony_ci#include <linux/sfp.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "ionic.h" 98c2ecf20Sopenharmony_ci#include "ionic_bus.h" 108c2ecf20Sopenharmony_ci#include "ionic_lif.h" 118c2ecf20Sopenharmony_ci#include "ionic_ethtool.h" 128c2ecf20Sopenharmony_ci#include "ionic_stats.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic const char ionic_priv_flags_strings[][ETH_GSTRING_LEN] = { 158c2ecf20Sopenharmony_ci#define IONIC_PRIV_F_SW_DBG_STATS BIT(0) 168c2ecf20Sopenharmony_ci "sw-dbg-stats", 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define IONIC_PRIV_FLAGS_COUNT ARRAY_SIZE(ionic_priv_flags_strings) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci u32 i; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci for (i = 0; i < ionic_num_stats_grps; i++) 268c2ecf20Sopenharmony_ci ionic_stats_groups[i].get_strings(lif, &buf); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void ionic_get_stats(struct net_device *netdev, 308c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *buf) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct ionic_lif *lif; 338c2ecf20Sopenharmony_ci u32 i; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci lif = netdev_priv(netdev); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci memset(buf, 0, stats->n_stats * sizeof(*buf)); 388c2ecf20Sopenharmony_ci for (i = 0; i < ionic_num_stats_grps; i++) 398c2ecf20Sopenharmony_ci ionic_stats_groups[i].get_values(lif, &buf); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int ionic_get_stats_count(struct ionic_lif *lif) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int i, num_stats = 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci for (i = 0; i < ionic_num_stats_grps; i++) 478c2ecf20Sopenharmony_ci num_stats += ionic_stats_groups[i].get_count(lif); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return num_stats; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int ionic_get_sset_count(struct net_device *netdev, int sset) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 558c2ecf20Sopenharmony_ci int count = 0; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci switch (sset) { 588c2ecf20Sopenharmony_ci case ETH_SS_STATS: 598c2ecf20Sopenharmony_ci count = ionic_get_stats_count(lif); 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 628c2ecf20Sopenharmony_ci count = IONIC_PRIV_FLAGS_COUNT; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci return count; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void ionic_get_strings(struct net_device *netdev, 698c2ecf20Sopenharmony_ci u32 sset, u8 *buf) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci switch (sset) { 748c2ecf20Sopenharmony_ci case ETH_SS_STATS: 758c2ecf20Sopenharmony_ci ionic_get_stats_strings(lif, buf); 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 788c2ecf20Sopenharmony_ci memcpy(buf, ionic_priv_flags_strings, 798c2ecf20Sopenharmony_ci IONIC_PRIV_FLAGS_COUNT * ETH_GSTRING_LEN); 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void ionic_get_drvinfo(struct net_device *netdev, 858c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 888c2ecf20Sopenharmony_ci struct ionic *ionic = lif->ionic; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); 918c2ecf20Sopenharmony_ci strlcpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, 928c2ecf20Sopenharmony_ci sizeof(drvinfo->fw_version)); 938c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, ionic_bus_info(ionic), 948c2ecf20Sopenharmony_ci sizeof(drvinfo->bus_info)); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int ionic_get_regs_len(struct net_device *netdev) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return (IONIC_DEV_INFO_REG_COUNT + IONIC_DEV_CMD_REG_COUNT) * sizeof(u32); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void ionic_get_regs(struct net_device *netdev, struct ethtool_regs *regs, 1038c2ecf20Sopenharmony_ci void *p) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 1068c2ecf20Sopenharmony_ci unsigned int offset; 1078c2ecf20Sopenharmony_ci unsigned int size; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci regs->version = IONIC_DEV_CMD_REG_VERSION; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci offset = 0; 1128c2ecf20Sopenharmony_ci size = IONIC_DEV_INFO_REG_COUNT * sizeof(u32); 1138c2ecf20Sopenharmony_ci memcpy_fromio(p + offset, lif->ionic->idev.dev_info_regs->words, size); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci offset += size; 1168c2ecf20Sopenharmony_ci size = IONIC_DEV_CMD_REG_COUNT * sizeof(u32); 1178c2ecf20Sopenharmony_ci memcpy_fromio(p + offset, lif->ionic->idev.dev_cmd_regs->words, size); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int ionic_get_link_ksettings(struct net_device *netdev, 1218c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *ks) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 1248c2ecf20Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 1258c2ecf20Sopenharmony_ci int copper_seen = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(ks, supported); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!idev->port_info) { 1308c2ecf20Sopenharmony_ci netdev_err(netdev, "port_info not initialized\n"); 1318c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* The port_info data is found in a DMA space that the NIC keeps 1358c2ecf20Sopenharmony_ci * up-to-date, so there's no need to request the data from the 1368c2ecf20Sopenharmony_ci * NIC, we already have it in our memory space. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci switch (le16_to_cpu(idev->port_info->status.xcvr.pid)) { 1408c2ecf20Sopenharmony_ci /* Copper */ 1418c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_CR4: 1428c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1438c2ecf20Sopenharmony_ci 100000baseCR4_Full); 1448c2ecf20Sopenharmony_ci copper_seen++; 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_40GBASE_CR4: 1478c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1488c2ecf20Sopenharmony_ci 40000baseCR4_Full); 1498c2ecf20Sopenharmony_ci copper_seen++; 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_25GBASE_CR_S: 1528c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_25GBASE_CR_L: 1538c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_25GBASE_CR_N: 1548c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1558c2ecf20Sopenharmony_ci 25000baseCR_Full); 1568c2ecf20Sopenharmony_ci copper_seen++; 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_10GBASE_AOC: 1598c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_10GBASE_CU: 1608c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1618c2ecf20Sopenharmony_ci 10000baseCR_Full); 1628c2ecf20Sopenharmony_ci copper_seen++; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Fibre */ 1668c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_SR4: 1678c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_AOC: 1688c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1698c2ecf20Sopenharmony_ci 100000baseSR4_Full); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_CWDM4: 1728c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_PSM4: 1738c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_LR4: 1748c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1758c2ecf20Sopenharmony_ci 100000baseLR4_ER4_Full); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_100G_ER4: 1788c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1798c2ecf20Sopenharmony_ci 100000baseLR4_ER4_Full); 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_40GBASE_SR4: 1828c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_40GBASE_AOC: 1838c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1848c2ecf20Sopenharmony_ci 40000baseSR4_Full); 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_QSFP_40GBASE_LR4: 1878c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1888c2ecf20Sopenharmony_ci 40000baseLR4_Full); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_25GBASE_SR: 1918c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_25GBASE_AOC: 1928c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_25GBASE_ACC: 1938c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1948c2ecf20Sopenharmony_ci 25000baseSR_Full); 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_10GBASE_SR: 1978c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 1988c2ecf20Sopenharmony_ci 10000baseSR_Full); 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_10GBASE_LR: 2018c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 2028c2ecf20Sopenharmony_ci 10000baseLR_Full); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_10GBASE_LRM: 2058c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 2068c2ecf20Sopenharmony_ci 10000baseLRM_Full); 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_SFP_10GBASE_ER: 2098c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, 2108c2ecf20Sopenharmony_ci 10000baseER_Full); 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case IONIC_XCVR_PID_UNKNOWN: 2138c2ecf20Sopenharmony_ci /* This means there's no module plugged in */ 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci default: 2168c2ecf20Sopenharmony_ci dev_info(lif->ionic->dev, "unknown xcvr type pid=%d / 0x%x\n", 2178c2ecf20Sopenharmony_ci idev->port_info->status.xcvr.pid, 2188c2ecf20Sopenharmony_ci idev->port_info->status.xcvr.pid); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci bitmap_copy(ks->link_modes.advertising, ks->link_modes.supported, 2238c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER); 2268c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS); 2278c2ecf20Sopenharmony_ci if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_FC) 2288c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_BASER); 2298c2ecf20Sopenharmony_ci else if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_RS) 2308c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); 2338c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, Pause); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_COPPER || 2368c2ecf20Sopenharmony_ci copper_seen) 2378c2ecf20Sopenharmony_ci ks->base.port = PORT_DA; 2388c2ecf20Sopenharmony_ci else if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_FIBER) 2398c2ecf20Sopenharmony_ci ks->base.port = PORT_FIBRE; 2408c2ecf20Sopenharmony_ci else 2418c2ecf20Sopenharmony_ci ks->base.port = PORT_NONE; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (ks->base.port != PORT_NONE) { 2448c2ecf20Sopenharmony_ci ks->base.speed = le32_to_cpu(lif->info->status.link_speed); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (le16_to_cpu(lif->info->status.link_status)) 2478c2ecf20Sopenharmony_ci ks->base.duplex = DUPLEX_FULL; 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci ks->base.duplex = DUPLEX_UNKNOWN; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (idev->port_info->config.an_enable) { 2548c2ecf20Sopenharmony_ci ethtool_link_ksettings_add_link_mode(ks, advertising, 2558c2ecf20Sopenharmony_ci Autoneg); 2568c2ecf20Sopenharmony_ci ks->base.autoneg = AUTONEG_ENABLE; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int ionic_set_link_ksettings(struct net_device *netdev, 2648c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *ks) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 2678c2ecf20Sopenharmony_ci struct ionic *ionic = lif->ionic; 2688c2ecf20Sopenharmony_ci struct ionic_dev *idev; 2698c2ecf20Sopenharmony_ci int err = 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci idev = &lif->ionic->idev; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* set autoneg */ 2748c2ecf20Sopenharmony_ci if (ks->base.autoneg != idev->port_info->config.an_enable) { 2758c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 2768c2ecf20Sopenharmony_ci ionic_dev_cmd_port_autoneg(idev, ks->base.autoneg); 2778c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 2788c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 2798c2ecf20Sopenharmony_ci if (err) 2808c2ecf20Sopenharmony_ci return err; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* set speed */ 2848c2ecf20Sopenharmony_ci if (ks->base.speed != le32_to_cpu(idev->port_info->config.speed)) { 2858c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 2868c2ecf20Sopenharmony_ci ionic_dev_cmd_port_speed(idev, ks->base.speed); 2878c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 2888c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 2898c2ecf20Sopenharmony_ci if (err) 2908c2ecf20Sopenharmony_ci return err; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void ionic_get_pauseparam(struct net_device *netdev, 2978c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 3008c2ecf20Sopenharmony_ci u8 pause_type; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci pause->autoneg = 0; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci pause_type = lif->ionic->idev.port_info->config.pause_type; 3058c2ecf20Sopenharmony_ci if (pause_type) { 3068c2ecf20Sopenharmony_ci pause->rx_pause = (pause_type & IONIC_PAUSE_F_RX) ? 1 : 0; 3078c2ecf20Sopenharmony_ci pause->tx_pause = (pause_type & IONIC_PAUSE_F_TX) ? 1 : 0; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int ionic_set_pauseparam(struct net_device *netdev, 3128c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 3158c2ecf20Sopenharmony_ci struct ionic *ionic = lif->ionic; 3168c2ecf20Sopenharmony_ci u32 requested_pause; 3178c2ecf20Sopenharmony_ci int err; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (pause->autoneg) 3208c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* change both at the same time */ 3238c2ecf20Sopenharmony_ci requested_pause = IONIC_PORT_PAUSE_TYPE_LINK; 3248c2ecf20Sopenharmony_ci if (pause->rx_pause) 3258c2ecf20Sopenharmony_ci requested_pause |= IONIC_PAUSE_F_RX; 3268c2ecf20Sopenharmony_ci if (pause->tx_pause) 3278c2ecf20Sopenharmony_ci requested_pause |= IONIC_PAUSE_F_TX; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (requested_pause == lif->ionic->idev.port_info->config.pause_type) 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 3338c2ecf20Sopenharmony_ci ionic_dev_cmd_port_pause(&lif->ionic->idev, requested_pause); 3348c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 3358c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 3368c2ecf20Sopenharmony_ci if (err) 3378c2ecf20Sopenharmony_ci return err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int ionic_get_fecparam(struct net_device *netdev, 3438c2ecf20Sopenharmony_ci struct ethtool_fecparam *fec) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci switch (lif->ionic->idev.port_info->config.fec_type) { 3488c2ecf20Sopenharmony_ci case IONIC_PORT_FEC_TYPE_NONE: 3498c2ecf20Sopenharmony_ci fec->active_fec = ETHTOOL_FEC_OFF; 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci case IONIC_PORT_FEC_TYPE_RS: 3528c2ecf20Sopenharmony_ci fec->active_fec = ETHTOOL_FEC_RS; 3538c2ecf20Sopenharmony_ci break; 3548c2ecf20Sopenharmony_ci case IONIC_PORT_FEC_TYPE_FC: 3558c2ecf20Sopenharmony_ci fec->active_fec = ETHTOOL_FEC_BASER; 3568c2ecf20Sopenharmony_ci break; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci fec->fec = ETHTOOL_FEC_OFF | ETHTOOL_FEC_RS | ETHTOOL_FEC_BASER; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int ionic_set_fecparam(struct net_device *netdev, 3658c2ecf20Sopenharmony_ci struct ethtool_fecparam *fec) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 3688c2ecf20Sopenharmony_ci u8 fec_type; 3698c2ecf20Sopenharmony_ci int ret = 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (lif->ionic->idev.port_info->config.an_enable) { 3728c2ecf20Sopenharmony_ci netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n"); 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci switch (fec->fec) { 3778c2ecf20Sopenharmony_ci case ETHTOOL_FEC_NONE: 3788c2ecf20Sopenharmony_ci fec_type = IONIC_PORT_FEC_TYPE_NONE; 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci case ETHTOOL_FEC_OFF: 3818c2ecf20Sopenharmony_ci fec_type = IONIC_PORT_FEC_TYPE_NONE; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci case ETHTOOL_FEC_RS: 3848c2ecf20Sopenharmony_ci fec_type = IONIC_PORT_FEC_TYPE_RS; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case ETHTOOL_FEC_BASER: 3878c2ecf20Sopenharmony_ci fec_type = IONIC_PORT_FEC_TYPE_FC; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case ETHTOOL_FEC_AUTO: 3908c2ecf20Sopenharmony_ci default: 3918c2ecf20Sopenharmony_ci netdev_err(netdev, "FEC request 0x%04x not supported\n", 3928c2ecf20Sopenharmony_ci fec->fec); 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (fec_type != lif->ionic->idev.port_info->config.fec_type) { 3978c2ecf20Sopenharmony_ci mutex_lock(&lif->ionic->dev_cmd_lock); 3988c2ecf20Sopenharmony_ci ionic_dev_cmd_port_fec(&lif->ionic->idev, fec_type); 3998c2ecf20Sopenharmony_ci ret = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); 4008c2ecf20Sopenharmony_ci mutex_unlock(&lif->ionic->dev_cmd_lock); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return ret; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int ionic_get_coalesce(struct net_device *netdev, 4078c2ecf20Sopenharmony_ci struct ethtool_coalesce *coalesce) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci coalesce->tx_coalesce_usecs = lif->tx_coalesce_usecs; 4128c2ecf20Sopenharmony_ci coalesce->rx_coalesce_usecs = lif->rx_coalesce_usecs; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 4158c2ecf20Sopenharmony_ci coalesce->use_adaptive_tx_coalesce = test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state); 4168c2ecf20Sopenharmony_ci else 4178c2ecf20Sopenharmony_ci coalesce->use_adaptive_tx_coalesce = 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci coalesce->use_adaptive_rx_coalesce = test_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int ionic_set_coalesce(struct net_device *netdev, 4258c2ecf20Sopenharmony_ci struct ethtool_coalesce *coalesce) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 4288c2ecf20Sopenharmony_ci struct ionic_identity *ident; 4298c2ecf20Sopenharmony_ci u32 rx_coal, rx_dim; 4308c2ecf20Sopenharmony_ci u32 tx_coal, tx_dim; 4318c2ecf20Sopenharmony_ci unsigned int i; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ident = &lif->ionic->ident; 4348c2ecf20Sopenharmony_ci if (ident->dev.intr_coal_div == 0) { 4358c2ecf20Sopenharmony_ci netdev_warn(netdev, "bad HW value in dev.intr_coal_div = %d\n", 4368c2ecf20Sopenharmony_ci ident->dev.intr_coal_div); 4378c2ecf20Sopenharmony_ci return -EIO; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Tx normally shares Rx interrupt, so only change Rx if not split */ 4418c2ecf20Sopenharmony_ci if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state) && 4428c2ecf20Sopenharmony_ci (coalesce->tx_coalesce_usecs != lif->rx_coalesce_usecs || 4438c2ecf20Sopenharmony_ci coalesce->use_adaptive_tx_coalesce)) { 4448c2ecf20Sopenharmony_ci netdev_warn(netdev, "only rx parameters can be changed\n"); 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Convert the usec request to a HW usable value. If they asked 4498c2ecf20Sopenharmony_ci * for non-zero and it resolved to zero, bump it up 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci rx_coal = ionic_coal_usec_to_hw(lif->ionic, coalesce->rx_coalesce_usecs); 4528c2ecf20Sopenharmony_ci if (!rx_coal && coalesce->rx_coalesce_usecs) 4538c2ecf20Sopenharmony_ci rx_coal = 1; 4548c2ecf20Sopenharmony_ci tx_coal = ionic_coal_usec_to_hw(lif->ionic, coalesce->tx_coalesce_usecs); 4558c2ecf20Sopenharmony_ci if (!tx_coal && coalesce->tx_coalesce_usecs) 4568c2ecf20Sopenharmony_ci tx_coal = 1; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (rx_coal > IONIC_INTR_CTRL_COAL_MAX || 4598c2ecf20Sopenharmony_ci tx_coal > IONIC_INTR_CTRL_COAL_MAX) 4608c2ecf20Sopenharmony_ci return -ERANGE; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Save the new values */ 4638c2ecf20Sopenharmony_ci lif->rx_coalesce_usecs = coalesce->rx_coalesce_usecs; 4648c2ecf20Sopenharmony_ci lif->rx_coalesce_hw = rx_coal; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 4678c2ecf20Sopenharmony_ci lif->tx_coalesce_usecs = coalesce->tx_coalesce_usecs; 4688c2ecf20Sopenharmony_ci else 4698c2ecf20Sopenharmony_ci lif->tx_coalesce_usecs = coalesce->rx_coalesce_usecs; 4708c2ecf20Sopenharmony_ci lif->tx_coalesce_hw = tx_coal; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (coalesce->use_adaptive_rx_coalesce) { 4738c2ecf20Sopenharmony_ci set_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state); 4748c2ecf20Sopenharmony_ci rx_dim = rx_coal; 4758c2ecf20Sopenharmony_ci } else { 4768c2ecf20Sopenharmony_ci clear_bit(IONIC_LIF_F_RX_DIM_INTR, lif->state); 4778c2ecf20Sopenharmony_ci rx_dim = 0; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (coalesce->use_adaptive_tx_coalesce) { 4818c2ecf20Sopenharmony_ci set_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state); 4828c2ecf20Sopenharmony_ci tx_dim = tx_coal; 4838c2ecf20Sopenharmony_ci } else { 4848c2ecf20Sopenharmony_ci clear_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state); 4858c2ecf20Sopenharmony_ci tx_dim = 0; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (test_bit(IONIC_LIF_F_UP, lif->state)) { 4898c2ecf20Sopenharmony_ci for (i = 0; i < lif->nxqs; i++) { 4908c2ecf20Sopenharmony_ci if (lif->rxqcqs[i]->flags & IONIC_QCQ_F_INTR) { 4918c2ecf20Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 4928c2ecf20Sopenharmony_ci lif->rxqcqs[i]->intr.index, 4938c2ecf20Sopenharmony_ci lif->rx_coalesce_hw); 4948c2ecf20Sopenharmony_ci lif->rxqcqs[i]->intr.dim_coal_hw = rx_dim; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (lif->txqcqs[i]->flags & IONIC_QCQ_F_INTR) { 4988c2ecf20Sopenharmony_ci ionic_intr_coal_init(lif->ionic->idev.intr_ctrl, 4998c2ecf20Sopenharmony_ci lif->txqcqs[i]->intr.index, 5008c2ecf20Sopenharmony_ci lif->tx_coalesce_hw); 5018c2ecf20Sopenharmony_ci lif->txqcqs[i]->intr.dim_coal_hw = tx_dim; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic void ionic_get_ringparam(struct net_device *netdev, 5108c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci ring->tx_max_pending = IONIC_MAX_TX_DESC; 5158c2ecf20Sopenharmony_ci ring->tx_pending = lif->ntxq_descs; 5168c2ecf20Sopenharmony_ci ring->rx_max_pending = IONIC_MAX_RX_DESC; 5178c2ecf20Sopenharmony_ci ring->rx_pending = lif->nrxq_descs; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int ionic_set_ringparam(struct net_device *netdev, 5218c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 5248c2ecf20Sopenharmony_ci struct ionic_queue_params qparam; 5258c2ecf20Sopenharmony_ci int err; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ionic_init_queue_params(lif, &qparam); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (ring->rx_mini_pending || ring->rx_jumbo_pending) { 5308c2ecf20Sopenharmony_ci netdev_info(netdev, "Changing jumbo or mini descriptors not supported\n"); 5318c2ecf20Sopenharmony_ci return -EINVAL; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (!is_power_of_2(ring->tx_pending) || 5358c2ecf20Sopenharmony_ci !is_power_of_2(ring->rx_pending)) { 5368c2ecf20Sopenharmony_ci netdev_info(netdev, "Descriptor count must be a power of 2\n"); 5378c2ecf20Sopenharmony_ci return -EINVAL; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* if nothing to do return success */ 5418c2ecf20Sopenharmony_ci if (ring->tx_pending == lif->ntxq_descs && 5428c2ecf20Sopenharmony_ci ring->rx_pending == lif->nrxq_descs) 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (ring->tx_pending != lif->ntxq_descs) 5468c2ecf20Sopenharmony_ci netdev_info(netdev, "Changing Tx ring size from %d to %d\n", 5478c2ecf20Sopenharmony_ci lif->ntxq_descs, ring->tx_pending); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (ring->rx_pending != lif->nrxq_descs) 5508c2ecf20Sopenharmony_ci netdev_info(netdev, "Changing Rx ring size from %d to %d\n", 5518c2ecf20Sopenharmony_ci lif->nrxq_descs, ring->rx_pending); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* if we're not running, just set the values and return */ 5548c2ecf20Sopenharmony_ci if (!netif_running(lif->netdev)) { 5558c2ecf20Sopenharmony_ci lif->ntxq_descs = ring->tx_pending; 5568c2ecf20Sopenharmony_ci lif->nrxq_descs = ring->rx_pending; 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci qparam.ntxq_descs = ring->tx_pending; 5618c2ecf20Sopenharmony_ci qparam.nrxq_descs = ring->rx_pending; 5628c2ecf20Sopenharmony_ci err = ionic_reconfigure_queues(lif, &qparam); 5638c2ecf20Sopenharmony_ci if (err) 5648c2ecf20Sopenharmony_ci netdev_info(netdev, "Ring reconfiguration failed, changes canceled: %d\n", err); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return err; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void ionic_get_channels(struct net_device *netdev, 5708c2ecf20Sopenharmony_ci struct ethtool_channels *ch) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* report maximum channels */ 5758c2ecf20Sopenharmony_ci ch->max_combined = lif->ionic->ntxqs_per_lif; 5768c2ecf20Sopenharmony_ci ch->max_rx = lif->ionic->ntxqs_per_lif / 2; 5778c2ecf20Sopenharmony_ci ch->max_tx = lif->ionic->ntxqs_per_lif / 2; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* report current channels */ 5808c2ecf20Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) { 5818c2ecf20Sopenharmony_ci ch->rx_count = lif->nxqs; 5828c2ecf20Sopenharmony_ci ch->tx_count = lif->nxqs; 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci ch->combined_count = lif->nxqs; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int ionic_set_channels(struct net_device *netdev, 5898c2ecf20Sopenharmony_ci struct ethtool_channels *ch) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 5928c2ecf20Sopenharmony_ci struct ionic_queue_params qparam; 5938c2ecf20Sopenharmony_ci int max_cnt; 5948c2ecf20Sopenharmony_ci int err; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci ionic_init_queue_params(lif, &qparam); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (ch->rx_count != ch->tx_count) { 5998c2ecf20Sopenharmony_ci netdev_info(netdev, "The rx and tx count must be equal\n"); 6008c2ecf20Sopenharmony_ci return -EINVAL; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (ch->combined_count && ch->rx_count) { 6048c2ecf20Sopenharmony_ci netdev_info(netdev, "Use either combined or rx and tx, not both\n"); 6058c2ecf20Sopenharmony_ci return -EINVAL; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci max_cnt = lif->ionic->ntxqs_per_lif; 6098c2ecf20Sopenharmony_ci if (ch->combined_count) { 6108c2ecf20Sopenharmony_ci if (ch->combined_count > max_cnt) 6118c2ecf20Sopenharmony_ci return -EINVAL; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 6148c2ecf20Sopenharmony_ci netdev_info(lif->netdev, "Sharing queue interrupts\n"); 6158c2ecf20Sopenharmony_ci else if (ch->combined_count == lif->nxqs) 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (lif->nxqs != ch->combined_count) 6198c2ecf20Sopenharmony_ci netdev_info(netdev, "Changing queue count from %d to %d\n", 6208c2ecf20Sopenharmony_ci lif->nxqs, ch->combined_count); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci qparam.nxqs = ch->combined_count; 6238c2ecf20Sopenharmony_ci qparam.intr_split = 0; 6248c2ecf20Sopenharmony_ci } else { 6258c2ecf20Sopenharmony_ci max_cnt /= 2; 6268c2ecf20Sopenharmony_ci if (ch->rx_count > max_cnt) 6278c2ecf20Sopenharmony_ci return -EINVAL; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) 6308c2ecf20Sopenharmony_ci netdev_info(lif->netdev, "Splitting queue interrupts\n"); 6318c2ecf20Sopenharmony_ci else if (ch->rx_count == lif->nxqs) 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (lif->nxqs != ch->rx_count) 6358c2ecf20Sopenharmony_ci netdev_info(netdev, "Changing queue count from %d to %d\n", 6368c2ecf20Sopenharmony_ci lif->nxqs, ch->rx_count); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci qparam.nxqs = ch->rx_count; 6398c2ecf20Sopenharmony_ci qparam.intr_split = 1; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* if we're not running, just set the values and return */ 6438c2ecf20Sopenharmony_ci if (!netif_running(lif->netdev)) { 6448c2ecf20Sopenharmony_ci lif->nxqs = qparam.nxqs; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (qparam.intr_split) { 6478c2ecf20Sopenharmony_ci set_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); 6488c2ecf20Sopenharmony_ci } else { 6498c2ecf20Sopenharmony_ci clear_bit(IONIC_LIF_F_SPLIT_INTR, lif->state); 6508c2ecf20Sopenharmony_ci lif->tx_coalesce_usecs = lif->rx_coalesce_usecs; 6518c2ecf20Sopenharmony_ci lif->tx_coalesce_hw = lif->rx_coalesce_hw; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci err = ionic_reconfigure_queues(lif, &qparam); 6578c2ecf20Sopenharmony_ci if (err) 6588c2ecf20Sopenharmony_ci netdev_info(netdev, "Queue reconfiguration failed, changes canceled: %d\n", err); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return err; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic u32 ionic_get_priv_flags(struct net_device *netdev) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 6668c2ecf20Sopenharmony_ci u32 priv_flags = 0; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (test_bit(IONIC_LIF_F_SW_DEBUG_STATS, lif->state)) 6698c2ecf20Sopenharmony_ci priv_flags |= IONIC_PRIV_F_SW_DBG_STATS; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return priv_flags; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int ionic_set_priv_flags(struct net_device *netdev, u32 priv_flags) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci clear_bit(IONIC_LIF_F_SW_DEBUG_STATS, lif->state); 6798c2ecf20Sopenharmony_ci if (priv_flags & IONIC_PRIV_F_SW_DBG_STATS) 6808c2ecf20Sopenharmony_ci set_bit(IONIC_LIF_F_SW_DEBUG_STATS, lif->state); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int ionic_get_rxnfc(struct net_device *netdev, 6868c2ecf20Sopenharmony_ci struct ethtool_rxnfc *info, u32 *rules) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 6898c2ecf20Sopenharmony_ci int err = 0; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci switch (info->cmd) { 6928c2ecf20Sopenharmony_ci case ETHTOOL_GRXRINGS: 6938c2ecf20Sopenharmony_ci info->data = lif->nxqs; 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci default: 6968c2ecf20Sopenharmony_ci netdev_dbg(netdev, "Command parameter %d is not supported\n", 6978c2ecf20Sopenharmony_ci info->cmd); 6988c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return err; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic u32 ionic_get_rxfh_indir_size(struct net_device *netdev) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic u32 ionic_get_rxfh_key_size(struct net_device *netdev) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci return IONIC_RSS_HASH_KEY_SIZE; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int ionic_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, 7178c2ecf20Sopenharmony_ci u8 *hfunc) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 7208c2ecf20Sopenharmony_ci unsigned int i, tbl_sz; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (indir) { 7238c2ecf20Sopenharmony_ci tbl_sz = le16_to_cpu(lif->ionic->ident.lif.eth.rss_ind_tbl_sz); 7248c2ecf20Sopenharmony_ci for (i = 0; i < tbl_sz; i++) 7258c2ecf20Sopenharmony_ci indir[i] = lif->rss_ind_tbl[i]; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (key) 7298c2ecf20Sopenharmony_ci memcpy(key, lif->rss_hash_key, IONIC_RSS_HASH_KEY_SIZE); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (hfunc) 7328c2ecf20Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return 0; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic int ionic_set_rxfh(struct net_device *netdev, const u32 *indir, 7388c2ecf20Sopenharmony_ci const u8 *key, const u8 hfunc) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 7418c2ecf20Sopenharmony_ci int err; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) 7448c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci err = ionic_lif_rss_config(lif, lif->rss_types, key, indir); 7478c2ecf20Sopenharmony_ci if (err) 7488c2ecf20Sopenharmony_ci return err; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return 0; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic int ionic_set_tunable(struct net_device *dev, 7548c2ecf20Sopenharmony_ci const struct ethtool_tunable *tuna, 7558c2ecf20Sopenharmony_ci const void *data) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(dev); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci switch (tuna->id) { 7608c2ecf20Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 7618c2ecf20Sopenharmony_ci lif->rx_copybreak = *(u32 *)data; 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci default: 7648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int ionic_get_tunable(struct net_device *netdev, 7718c2ecf20Sopenharmony_ci const struct ethtool_tunable *tuna, void *data) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci switch (tuna->id) { 7768c2ecf20Sopenharmony_ci case ETHTOOL_RX_COPYBREAK: 7778c2ecf20Sopenharmony_ci *(u32 *)data = lif->rx_copybreak; 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci default: 7808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci return 0; 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic int ionic_get_module_info(struct net_device *netdev, 7878c2ecf20Sopenharmony_ci struct ethtool_modinfo *modinfo) 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 7918c2ecf20Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 7928c2ecf20Sopenharmony_ci struct ionic_xcvr_status *xcvr; 7938c2ecf20Sopenharmony_ci struct sfp_eeprom_base *sfp; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci xcvr = &idev->port_info->status.xcvr; 7968c2ecf20Sopenharmony_ci sfp = (struct sfp_eeprom_base *) xcvr->sprom; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* report the module data type and length */ 7998c2ecf20Sopenharmony_ci switch (sfp->phys_id) { 8008c2ecf20Sopenharmony_ci case SFF8024_ID_SFP: 8018c2ecf20Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8079; 8028c2ecf20Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; 8038c2ecf20Sopenharmony_ci break; 8048c2ecf20Sopenharmony_ci case SFF8024_ID_QSFP_8436_8636: 8058c2ecf20Sopenharmony_ci case SFF8024_ID_QSFP28_8636: 8068c2ecf20Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8436; 8078c2ecf20Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; 8088c2ecf20Sopenharmony_ci break; 8098c2ecf20Sopenharmony_ci default: 8108c2ecf20Sopenharmony_ci netdev_info(netdev, "unknown xcvr type 0x%02x\n", 8118c2ecf20Sopenharmony_ci xcvr->sprom[0]); 8128c2ecf20Sopenharmony_ci modinfo->type = 0; 8138c2ecf20Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return 0; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic int ionic_get_module_eeprom(struct net_device *netdev, 8218c2ecf20Sopenharmony_ci struct ethtool_eeprom *ee, 8228c2ecf20Sopenharmony_ci u8 *data) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 8258c2ecf20Sopenharmony_ci struct ionic_dev *idev = &lif->ionic->idev; 8268c2ecf20Sopenharmony_ci struct ionic_xcvr_status *xcvr; 8278c2ecf20Sopenharmony_ci char tbuf[sizeof(xcvr->sprom)]; 8288c2ecf20Sopenharmony_ci int count = 10; 8298c2ecf20Sopenharmony_ci u32 len; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* The NIC keeps the module prom up-to-date in the DMA space 8328c2ecf20Sopenharmony_ci * so we can simply copy the module bytes into the data buffer. 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_ci xcvr = &idev->port_info->status.xcvr; 8358c2ecf20Sopenharmony_ci len = min_t(u32, sizeof(xcvr->sprom), ee->len); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci do { 8388c2ecf20Sopenharmony_ci memcpy(data, xcvr->sprom, len); 8398c2ecf20Sopenharmony_ci memcpy(tbuf, xcvr->sprom, len); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* Let's make sure we got a consistent copy */ 8428c2ecf20Sopenharmony_ci if (!memcmp(data, tbuf, len)) 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci } while (--count); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (!count) 8488c2ecf20Sopenharmony_ci return -ETIMEDOUT; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return 0; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic int ionic_nway_reset(struct net_device *netdev) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 8568c2ecf20Sopenharmony_ci struct ionic *ionic = lif->ionic; 8578c2ecf20Sopenharmony_ci int err = 0; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* flap the link to force auto-negotiation */ 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_DOWN); 8648c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (!err) { 8678c2ecf20Sopenharmony_ci ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_UP); 8688c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return err; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic const struct ethtool_ops ionic_ethtool_ops = { 8778c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 8788c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_RX | 8798c2ecf20Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_TX, 8808c2ecf20Sopenharmony_ci .get_drvinfo = ionic_get_drvinfo, 8818c2ecf20Sopenharmony_ci .get_regs_len = ionic_get_regs_len, 8828c2ecf20Sopenharmony_ci .get_regs = ionic_get_regs, 8838c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 8848c2ecf20Sopenharmony_ci .get_link_ksettings = ionic_get_link_ksettings, 8858c2ecf20Sopenharmony_ci .set_link_ksettings = ionic_set_link_ksettings, 8868c2ecf20Sopenharmony_ci .get_coalesce = ionic_get_coalesce, 8878c2ecf20Sopenharmony_ci .set_coalesce = ionic_set_coalesce, 8888c2ecf20Sopenharmony_ci .get_ringparam = ionic_get_ringparam, 8898c2ecf20Sopenharmony_ci .set_ringparam = ionic_set_ringparam, 8908c2ecf20Sopenharmony_ci .get_channels = ionic_get_channels, 8918c2ecf20Sopenharmony_ci .set_channels = ionic_set_channels, 8928c2ecf20Sopenharmony_ci .get_strings = ionic_get_strings, 8938c2ecf20Sopenharmony_ci .get_ethtool_stats = ionic_get_stats, 8948c2ecf20Sopenharmony_ci .get_sset_count = ionic_get_sset_count, 8958c2ecf20Sopenharmony_ci .get_priv_flags = ionic_get_priv_flags, 8968c2ecf20Sopenharmony_ci .set_priv_flags = ionic_set_priv_flags, 8978c2ecf20Sopenharmony_ci .get_rxnfc = ionic_get_rxnfc, 8988c2ecf20Sopenharmony_ci .get_rxfh_indir_size = ionic_get_rxfh_indir_size, 8998c2ecf20Sopenharmony_ci .get_rxfh_key_size = ionic_get_rxfh_key_size, 9008c2ecf20Sopenharmony_ci .get_rxfh = ionic_get_rxfh, 9018c2ecf20Sopenharmony_ci .set_rxfh = ionic_set_rxfh, 9028c2ecf20Sopenharmony_ci .get_tunable = ionic_get_tunable, 9038c2ecf20Sopenharmony_ci .set_tunable = ionic_set_tunable, 9048c2ecf20Sopenharmony_ci .get_module_info = ionic_get_module_info, 9058c2ecf20Sopenharmony_ci .get_module_eeprom = ionic_get_module_eeprom, 9068c2ecf20Sopenharmony_ci .get_pauseparam = ionic_get_pauseparam, 9078c2ecf20Sopenharmony_ci .set_pauseparam = ionic_set_pauseparam, 9088c2ecf20Sopenharmony_ci .get_fecparam = ionic_get_fecparam, 9098c2ecf20Sopenharmony_ci .set_fecparam = ionic_set_fecparam, 9108c2ecf20Sopenharmony_ci .nway_reset = ionic_nway_reset, 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_civoid ionic_ethtool_set_ops(struct net_device *netdev) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci netdev->ethtool_ops = &ionic_ethtool_ops; 9168c2ecf20Sopenharmony_ci} 917