// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/module.h>#include <linux/netdevice.h>#include <linux/sfp.h> #include "ionic.h"#include "ionic_bus.h"#include "ionic_lif.h"#include "ionic_ethtool.h"#include "ionic_stats.h" #define IONIC_MAX_RX_COPYBREAK min(U16_MAX, IONIC_MAX_BUF_LEN) static void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf){ u32 i; for (i = 0; i < ionic_num_stats_grps; i++) ionic_stats_groups[i].get_strings(lif, &buf);} static void ionic_get_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *buf){ struct ionic_lif *lif = netdev_priv(netdev); u32 i; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return; memset(buf, 0, stats->n_stats * sizeof(*buf)); for (i = 0; i < ionic_num_stats_grps; i++) ionic_stats_groups[i].get_values(lif, &buf);} static int ionic_get_stats_count(struct ionic_lif *lif){ int i, num_stats = 0; for (i = 0; i < ionic_num_stats_grps; i++) num_stats += ionic_stats_groups[i].get_count(lif); return num_stats;} static int ionic_get_sset_count(struct net_device *netdev, int sset){ struct ionic_lif *lif = netdev_priv(netdev); int count = 0; switch (sset) { case ETH_SS_STATS: count = ionic_get_stats_count(lif); break; } return count;} static void ionic_get_strings(struct net_device *netdev, u32 sset, u8 *buf){ struct ionic_lif *lif = netdev_priv(netdev); switch (sset) { case ETH_SS_STATS: ionic_get_stats_strings(lif, buf); break; }} static void ionic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo){ struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; strscpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, sizeof(drvinfo->fw_version)); strscpy(drvinfo->bus_info, ionic_bus_info(ionic), sizeof(drvinfo->bus_info));} static int ionic_get_regs_len(struct net_device *netdev){ return (IONIC_DEV_INFO_REG_COUNT + IONIC_DEV_CMD_REG_COUNT) * sizeof(u32);} static void ionic_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p){ struct ionic_lif *lif = netdev_priv(netdev); struct ionic_dev *idev; unsigned int offset; unsigned int size; regs->version = IONIC_DEV_CMD_REG_VERSION; idev = &lif->ionic->idev; if (!idev->dev_info_regs) return; offset = 0; size = IONIC_DEV_INFO_REG_COUNT * sizeof(u32); memcpy_fromio(p + offset, lif->ionic->idev.dev_info_regs->words, size); offset += size; size = IONIC_DEV_CMD_REG_COUNT * sizeof(u32); memcpy_fromio(p + offset, idev->dev_cmd_regs->words, size);} static void ionic_get_link_ext_stats(struct net_device *netdev, struct ethtool_link_ext_stats *stats){ struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; u64 link_down_count_total; u16 link_down_count_fw; if (ionic->pdev->is_virtfn) return; if (!ionic->idev.port_info) { netdev_err_once(netdev, "port_info not initialized\n"); return; } link_down_count_fw = le16_to_cpu(ionic->idev.port_info->status.link_down_count); link_down_count_total = ionic->idev.link_down_count_total + link_down_count_fw - ionic->idev.link_down_count_last; /* The firmware counter is only 16 bits and can wraparound */ if (link_down_count_fw < ionic->idev.link_down_count_last) link_down_count_total += BIT(16); ionic->idev.link_down_count_last = link_down_count_fw; ionic->idev.link_down_count_total = link_down_count_total; stats->link_down_events = link_down_count_total;} static int ionic_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *ks){ struct ionic_lif *lif = netdev_priv(netdev); struct ionic_dev *idev = &lif->ionic->idev; int copper_seen = 0; ethtool_link_ksettings_zero_link_mode(ks, supported); if (!idev->port_info) { netdev_err(netdev, "port_info not initialized\n"); return -EOPNOTSUPP; } /* The port_info data is found in a DMA space that the NIC keeps * up-to-date, so there's no need to request the data from the * NIC, we already have it in our memory space. */ switch (le16_to_cpu(idev->port_info->status.xcvr.pid)) { /* Copper */ case IONIC_XCVR_PID_QSFP_100G_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 100000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_40GBASE_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_SFP_25GBASE_CR_S: case IONIC_XCVR_PID_SFP_25GBASE_CR_L: case IONIC_XCVR_PID_SFP_25GBASE_CR_N: ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseCR_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_50G_CR2_FC: case IONIC_XCVR_PID_QSFP_50G_CR2: ethtool_link_ksettings_add_link_mode(ks, supported, 50000baseCR2_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_200G_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_400G_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_SFP_10GBASE_AOC: case IONIC_XCVR_PID_SFP_10GBASE_CU: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseCR_Full); copper_seen++; break; /* Fibre */ case IONIC_XCVR_PID_QSFP_100G_SR4: case IONIC_XCVR_PID_QSFP_100G_AOC: ethtool_link_ksettings_add_link_mode(ks, supported, 100000baseSR4_Full); break; case IONIC_XCVR_PID_QSFP_100G_CWDM4: case IONIC_XCVR_PID_QSFP_100G_PSM4: case IONIC_XCVR_PID_QSFP_100G_LR4: case IONIC_XCVR_PID_QSFP_100G_ER4: case IONIC_XCVR_PID_QSFP_100G_FR4: case IONIC_XCVR_PID_QSFP_100G_DR4: ethtool_link_ksettings_add_link_mode(ks, supported, 100000baseLR4_ER4_Full); break; case IONIC_XCVR_PID_QSFP_40GBASE_SR4: case IONIC_XCVR_PID_QSFP_40GBASE_AOC: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseSR4_Full); break; case IONIC_XCVR_PID_QSFP_40GBASE_LR4: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseLR4_Full); break; case IONIC_XCVR_PID_SFP_25GBASE_SR: case IONIC_XCVR_PID_SFP_25GBASE_AOC: case IONIC_XCVR_PID_SFP_25GBASE_ACC: ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseSR_Full); break; case IONIC_XCVR_PID_QSFP_200G_AOC: case IONIC_XCVR_PID_QSFP_200G_SR4: case IONIC_XCVR_PID_QSFP_200G_AEC: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseSR4_Full); break; case IONIC_XCVR_PID_QSFP_200G_FR4: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseLR4_ER4_FR4_Full); break; case IONIC_XCVR_PID_QSFP_200G_DR4: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseDR4_Full); break; case IONIC_XCVR_PID_QSFP_400G_FR4: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseLR4_ER4_FR4_Full); break; case IONIC_XCVR_PID_QSFP_400G_DR4: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseDR4_Full); break; case IONIC_XCVR_PID_QSFP_400G_SR4: case IONIC_XCVR_PID_QSFP_400G_AOC: case IONIC_XCVR_PID_QSFP_400G_AEC: case IONIC_XCVR_PID_QSFP_400G_LPO: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseSR4_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_SR: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_LR: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLR_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_LRM: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLRM_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_ER: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseER_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_T: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full); break; case IONIC_XCVR_PID_SFP_1000BASE_T: ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); break; case IONIC_XCVR_PID_UNKNOWN: /* This means there's no module plugged in */ break; default: dev_dbg_ratelimited(lif->ionic->dev, "unknown xcvr type pid=%d / 0x%x\n", idev->port_info->status.xcvr.pid, idev->port_info->status.xcvr.pid); break; } linkmode_copy(ks->link_modes.advertising, ks->link_modes.supported); ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER); ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS); if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_FC) ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_BASER); else if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_RS) ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS); ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); ethtool_link_ksettings_add_link_mode(ks, supported, Pause); if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_COPPER || copper_seen) ks->base.port = PORT_DA; else if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_FIBER) ks->base.port = PORT_FIBRE; else ks->base.port = PORT_NONE; if (ks->base.port != PORT_NONE) { ks->base.speed = le32_to_cpu(lif->info->status.link_speed); if (le16_to_cpu(lif->info->status.link_status)) ks->base.duplex = DUPLEX_FULL; else ks->base.duplex = DUPLEX_UNKNOWN; ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); if (idev->port_info->config.an_enable) { ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ks->base.autoneg = AUTONEG_ENABLE; } } return 0;} static int ionic_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *ks){ struct ionic_lif *lif = netdev_priv(netdev); struct ionic_dev *idev = &lif->ionic->idev; struct ionic *ionic = lif->ionic; int err = 0; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return -EBUSY; /* set autoneg */ if (ks->base.autoneg != idev->port_info->config.an_enable) { mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_port_autoneg(idev, ks->base.autoneg); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); mutex_unlock(&ionic->dev_cmd_lock); if (err) return err; } /* set speed */ if (ks->base.speed != le32_to_cpu(idev->port_info->config.speed)) { mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_port_speed(idev, ks->base.speed); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); mutex_unlock(&ionic->dev_cmd_lock); if (err) return err; } return 0;} static void ionic_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause){ struct ionic_lif *lif = netdev_priv(netdev); u8 pause_type; pause->autoneg = 0; pause_type = lif->ionic->idev.port_info->config.pause_type; if (pause_type) { pause->rx_pause = (pause_type & IONIC_PAUSE_F_RX) ? 1 : 0; pause->tx_pause = (pause_type & IONIC_PAUSE_F_TX) ? 1 : 0; }} static int ionic_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause){ struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; u32 requested_pause; int err; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return -EBUSY; if (pause->autoneg) return -EOPNOTSUPP; /* change both at the same time */ requested_pause = IONIC_PORT_PAUSE_TYPE_LINK; if (pause->rx_pause) requested_pause |= IONIC_PAUSE_F_RX; if (pause->tx_pause) requested_pause |= IONIC_PAUSE_F_TX; if (requested_pause == lif->ionic->idev.port_info->config.pause_type) return 0; mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_port_pause(&lif->ionic->idev, requested_pause); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); mutex_unlock(&ionic->dev_cmd_lock); if (err) return err; return 0;} static int ionic_get_fecparam(struct net_device *netdev, struct ethtool_fecparam *fec){ struct ionic_lif *lif = netdev_priv(netdev); switch (lif->ionic->idev.port_info->config.fec_type) { case IONIC_PORT_FEC_TYPE_NONE: fec->active_fec = ETHTOOL_FEC_OFF; break; case IONIC_PORT_FEC_TYPE_RS: fec->active_fec = ETHTOOL_FEC_RS; break; case IONIC_PORT_FEC_TYPE_FC: fec->active_fec = ETHTOOL_FEC_BASER; break; } fec->fec = ETHTOOL_FEC_OFF | ETHTOOL_FEC_RS | ETHTOOL_FEC_BASER; return 0;} static int ionic_set_fecparam(struct net_device *netdev, struct ethtool_fecparam *fec){ struct ionic_lif *lif = netdev_priv(netdev); u8 fec_type; int ret = 0; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return -EBUSY; if (lif->ionic->idev.port_info->config.an_enable) { netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n"); return -EINVAL; } switch (fec->fec) { case ETHTOOL_FEC_NONE: fec_type = IONIC_PORT_FEC_TYPE_NONE; break; case ETHTOOL_FEC_OFF: fec_type = IONIC_PORT_FEC_TYPE_NONE; break; case ETHTOOL_FEC_RS: fec_type = IONIC_PORT_FEC_TYPE_RS; break; case ETHTOOL_FEC_BASER: fec_type = IONIC_PORT_FEC_TYPE_FC; break; case ETHTOOL_FEC_AUTO: default: netdev_err(netdev, "FEC request 0x%04x not supported\n", fec->fec); return -EINVAL; } if (fec_type != lif->ionic->idev.port_info->config.fec_type) { mutex_lock(&lif->ionic->dev_cmd_lock); ionic_dev_cmd_port_fec(&lif->ionic->idev, fec_type); ret = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); mutex_unlock(&lif->ionic->dev_cmd_lock); } return ret;} static int ionic_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack){ struct ionic_lif *lif = netdev_priv(netdev); coalesce->tx_coalesce_usecs = lif->tx_coalesce_usecs; coalesce->rx_coalesce_usecs = lif->rx_coalesce_usecs; if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) coalesce->use_adaptive_tx_coalesce = test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state);Lines 1–500 of 1193. Load more
// SPDX-License-Identifier: GPL-2.0 /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ #include <linux/module.h> #include <linux/netdevice.h> #include <linux/sfp.h> #include "ionic.h" #include "ionic_bus.h" #include "ionic_lif.h" #include "ionic_ethtool.h" #include "ionic_stats.h" #define IONIC_MAX_RX_COPYBREAK min(U16_MAX, IONIC_MAX_BUF_LEN) static void ionic_get_stats_strings(struct ionic_lif *lif, u8 *buf) { u32 i; for (i = 0; i < ionic_num_stats_grps; i++) ionic_stats_groups[i].get_strings(lif, &buf); } static void ionic_get_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *buf) { struct ionic_lif *lif = netdev_priv(netdev); u32 i; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return; memset(buf, 0, stats->n_stats * sizeof(*buf)); for (i = 0; i < ionic_num_stats_grps; i++) ionic_stats_groups[i].get_values(lif, &buf); } static int ionic_get_stats_count(struct ionic_lif *lif) { int i, num_stats = 0; for (i = 0; i < ionic_num_stats_grps; i++) num_stats += ionic_stats_groups[i].get_count(lif); return num_stats; } static int ionic_get_sset_count(struct net_device *netdev, int sset) { struct ionic_lif *lif = netdev_priv(netdev); int count = 0; switch (sset) { case ETH_SS_STATS: count = ionic_get_stats_count(lif); break; } return count; } static void ionic_get_strings(struct net_device *netdev, u32 sset, u8 *buf) { struct ionic_lif *lif = netdev_priv(netdev); switch (sset) { case ETH_SS_STATS: ionic_get_stats_strings(lif, buf); break; } } static void ionic_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; strscpy(drvinfo->driver, IONIC_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->fw_version, ionic->idev.dev_info.fw_version, sizeof(drvinfo->fw_version)); strscpy(drvinfo->bus_info, ionic_bus_info(ionic), sizeof(drvinfo->bus_info)); } static int ionic_get_regs_len(struct net_device *netdev) { return (IONIC_DEV_INFO_REG_COUNT + IONIC_DEV_CMD_REG_COUNT) * sizeof(u32); } static void ionic_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic_dev *idev; unsigned int offset; unsigned int size; regs->version = IONIC_DEV_CMD_REG_VERSION; idev = &lif->ionic->idev; if (!idev->dev_info_regs) return; offset = 0; size = IONIC_DEV_INFO_REG_COUNT * sizeof(u32); memcpy_fromio(p + offset, lif->ionic->idev.dev_info_regs->words, size); offset += size; size = IONIC_DEV_CMD_REG_COUNT * sizeof(u32); memcpy_fromio(p + offset, idev->dev_cmd_regs->words, size); } static void ionic_get_link_ext_stats(struct net_device *netdev, struct ethtool_link_ext_stats *stats) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; u64 link_down_count_total; u16 link_down_count_fw; if (ionic->pdev->is_virtfn) return; if (!ionic->idev.port_info) { netdev_err_once(netdev, "port_info not initialized\n"); return; } link_down_count_fw = le16_to_cpu(ionic->idev.port_info->status.link_down_count); link_down_count_total = ionic->idev.link_down_count_total + link_down_count_fw - ionic->idev.link_down_count_last; /* The firmware counter is only 16 bits and can wraparound */ if (link_down_count_fw < ionic->idev.link_down_count_last) link_down_count_total += BIT(16); ionic->idev.link_down_count_last = link_down_count_fw; ionic->idev.link_down_count_total = link_down_count_total; stats->link_down_events = link_down_count_total; } static int ionic_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *ks) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic_dev *idev = &lif->ionic->idev; int copper_seen = 0; ethtool_link_ksettings_zero_link_mode(ks, supported); if (!idev->port_info) { netdev_err(netdev, "port_info not initialized\n"); return -EOPNOTSUPP; } /* The port_info data is found in a DMA space that the NIC keeps * up-to-date, so there's no need to request the data from the * NIC, we already have it in our memory space. */ switch (le16_to_cpu(idev->port_info->status.xcvr.pid)) { /* Copper */ case IONIC_XCVR_PID_QSFP_100G_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 100000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_40GBASE_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_SFP_25GBASE_CR_S: case IONIC_XCVR_PID_SFP_25GBASE_CR_L: case IONIC_XCVR_PID_SFP_25GBASE_CR_N: ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseCR_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_50G_CR2_FC: case IONIC_XCVR_PID_QSFP_50G_CR2: ethtool_link_ksettings_add_link_mode(ks, supported, 50000baseCR2_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_200G_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_QSFP_400G_CR4: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseCR4_Full); copper_seen++; break; case IONIC_XCVR_PID_SFP_10GBASE_AOC: case IONIC_XCVR_PID_SFP_10GBASE_CU: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseCR_Full); copper_seen++; break; /* Fibre */ case IONIC_XCVR_PID_QSFP_100G_SR4: case IONIC_XCVR_PID_QSFP_100G_AOC: ethtool_link_ksettings_add_link_mode(ks, supported, 100000baseSR4_Full); break; case IONIC_XCVR_PID_QSFP_100G_CWDM4: case IONIC_XCVR_PID_QSFP_100G_PSM4: case IONIC_XCVR_PID_QSFP_100G_LR4: case IONIC_XCVR_PID_QSFP_100G_ER4: case IONIC_XCVR_PID_QSFP_100G_FR4: case IONIC_XCVR_PID_QSFP_100G_DR4: ethtool_link_ksettings_add_link_mode(ks, supported, 100000baseLR4_ER4_Full); break; case IONIC_XCVR_PID_QSFP_40GBASE_SR4: case IONIC_XCVR_PID_QSFP_40GBASE_AOC: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseSR4_Full); break; case IONIC_XCVR_PID_QSFP_40GBASE_LR4: ethtool_link_ksettings_add_link_mode(ks, supported, 40000baseLR4_Full); break; case IONIC_XCVR_PID_SFP_25GBASE_SR: case IONIC_XCVR_PID_SFP_25GBASE_AOC: case IONIC_XCVR_PID_SFP_25GBASE_ACC: ethtool_link_ksettings_add_link_mode(ks, supported, 25000baseSR_Full); break; case IONIC_XCVR_PID_QSFP_200G_AOC: case IONIC_XCVR_PID_QSFP_200G_SR4: case IONIC_XCVR_PID_QSFP_200G_AEC: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseSR4_Full); break; case IONIC_XCVR_PID_QSFP_200G_FR4: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseLR4_ER4_FR4_Full); break; case IONIC_XCVR_PID_QSFP_200G_DR4: ethtool_link_ksettings_add_link_mode(ks, supported, 200000baseDR4_Full); break; case IONIC_XCVR_PID_QSFP_400G_FR4: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseLR4_ER4_FR4_Full); break; case IONIC_XCVR_PID_QSFP_400G_DR4: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseDR4_Full); break; case IONIC_XCVR_PID_QSFP_400G_SR4: case IONIC_XCVR_PID_QSFP_400G_AOC: case IONIC_XCVR_PID_QSFP_400G_AEC: case IONIC_XCVR_PID_QSFP_400G_LPO: ethtool_link_ksettings_add_link_mode(ks, supported, 400000baseSR4_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_SR: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseSR_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_LR: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLR_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_LRM: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseLRM_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_ER: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseER_Full); break; case IONIC_XCVR_PID_SFP_10GBASE_T: ethtool_link_ksettings_add_link_mode(ks, supported, 10000baseT_Full); break; case IONIC_XCVR_PID_SFP_1000BASE_T: ethtool_link_ksettings_add_link_mode(ks, supported, 1000baseT_Full); break; case IONIC_XCVR_PID_UNKNOWN: /* This means there's no module plugged in */ break; default: dev_dbg_ratelimited(lif->ionic->dev, "unknown xcvr type pid=%d / 0x%x\n", idev->port_info->status.xcvr.pid, idev->port_info->status.xcvr.pid); break; } linkmode_copy(ks->link_modes.advertising, ks->link_modes.supported); ethtool_link_ksettings_add_link_mode(ks, supported, FEC_BASER); ethtool_link_ksettings_add_link_mode(ks, supported, FEC_RS); if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_FC) ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_BASER); else if (idev->port_info->config.fec_type == IONIC_PORT_FEC_TYPE_RS) ethtool_link_ksettings_add_link_mode(ks, advertising, FEC_RS); ethtool_link_ksettings_add_link_mode(ks, supported, FIBRE); ethtool_link_ksettings_add_link_mode(ks, supported, Pause); if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_COPPER || copper_seen) ks->base.port = PORT_DA; else if (idev->port_info->status.xcvr.phy == IONIC_PHY_TYPE_FIBER) ks->base.port = PORT_FIBRE; else ks->base.port = PORT_NONE; if (ks->base.port != PORT_NONE) { ks->base.speed = le32_to_cpu(lif->info->status.link_speed); if (le16_to_cpu(lif->info->status.link_status)) ks->base.duplex = DUPLEX_FULL; else ks->base.duplex = DUPLEX_UNKNOWN; ethtool_link_ksettings_add_link_mode(ks, supported, Autoneg); if (idev->port_info->config.an_enable) { ethtool_link_ksettings_add_link_mode(ks, advertising, Autoneg); ks->base.autoneg = AUTONEG_ENABLE; } } return 0; } static int ionic_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *ks) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic_dev *idev = &lif->ionic->idev; struct ionic *ionic = lif->ionic; int err = 0; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return -EBUSY; /* set autoneg */ if (ks->base.autoneg != idev->port_info->config.an_enable) { mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_port_autoneg(idev, ks->base.autoneg); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); mutex_unlock(&ionic->dev_cmd_lock); if (err) return err; } /* set speed */ if (ks->base.speed != le32_to_cpu(idev->port_info->config.speed)) { mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_port_speed(idev, ks->base.speed); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); mutex_unlock(&ionic->dev_cmd_lock); if (err) return err; } return 0; } static void ionic_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct ionic_lif *lif = netdev_priv(netdev); u8 pause_type; pause->autoneg = 0; pause_type = lif->ionic->idev.port_info->config.pause_type; if (pause_type) { pause->rx_pause = (pause_type & IONIC_PAUSE_F_RX) ? 1 : 0; pause->tx_pause = (pause_type & IONIC_PAUSE_F_TX) ? 1 : 0; } } static int ionic_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic *ionic = lif->ionic; u32 requested_pause; int err; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return -EBUSY; if (pause->autoneg) return -EOPNOTSUPP; /* change both at the same time */ requested_pause = IONIC_PORT_PAUSE_TYPE_LINK; if (pause->rx_pause) requested_pause |= IONIC_PAUSE_F_RX; if (pause->tx_pause) requested_pause |= IONIC_PAUSE_F_TX; if (requested_pause == lif->ionic->idev.port_info->config.pause_type) return 0; mutex_lock(&ionic->dev_cmd_lock); ionic_dev_cmd_port_pause(&lif->ionic->idev, requested_pause); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); mutex_unlock(&ionic->dev_cmd_lock); if (err) return err; return 0; } static int ionic_get_fecparam(struct net_device *netdev, struct ethtool_fecparam *fec) { struct ionic_lif *lif = netdev_priv(netdev); switch (lif->ionic->idev.port_info->config.fec_type) { case IONIC_PORT_FEC_TYPE_NONE: fec->active_fec = ETHTOOL_FEC_OFF; break; case IONIC_PORT_FEC_TYPE_RS: fec->active_fec = ETHTOOL_FEC_RS; break; case IONIC_PORT_FEC_TYPE_FC: fec->active_fec = ETHTOOL_FEC_BASER; break; } fec->fec = ETHTOOL_FEC_OFF | ETHTOOL_FEC_RS | ETHTOOL_FEC_BASER; return 0; } static int ionic_set_fecparam(struct net_device *netdev, struct ethtool_fecparam *fec) { struct ionic_lif *lif = netdev_priv(netdev); u8 fec_type; int ret = 0; if (test_bit(IONIC_LIF_F_FW_RESET, lif->state)) return -EBUSY; if (lif->ionic->idev.port_info->config.an_enable) { netdev_err(netdev, "FEC request not allowed while autoneg is enabled\n"); return -EINVAL; } switch (fec->fec) { case ETHTOOL_FEC_NONE: fec_type = IONIC_PORT_FEC_TYPE_NONE; break; case ETHTOOL_FEC_OFF: fec_type = IONIC_PORT_FEC_TYPE_NONE; break; case ETHTOOL_FEC_RS: fec_type = IONIC_PORT_FEC_TYPE_RS; break; case ETHTOOL_FEC_BASER: fec_type = IONIC_PORT_FEC_TYPE_FC; break; case ETHTOOL_FEC_AUTO: default: netdev_err(netdev, "FEC request 0x%04x not supported\n", fec->fec); return -EINVAL; } if (fec_type != lif->ionic->idev.port_info->config.fec_type) { mutex_lock(&lif->ionic->dev_cmd_lock); ionic_dev_cmd_port_fec(&lif->ionic->idev, fec_type); ret = ionic_dev_cmd_wait(lif->ionic, DEVCMD_TIMEOUT); mutex_unlock(&lif->ionic->dev_cmd_lock); } return ret; } static int ionic_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { struct ionic_lif *lif = netdev_priv(netdev); coalesce->tx_coalesce_usecs = lif->tx_coalesce_usecs; coalesce->rx_coalesce_usecs = lif->rx_coalesce_usecs; if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) coalesce->use_adaptive_tx_coalesce = test_bit(IONIC_LIF_F_TX_DIM_INTR, lif->state);