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