517 lines
16 KiB
C
517 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/* A network driver using virtio.
|
|
*
|
|
* Copyright 2007 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation
|
|
*/
|
|
//#define DEBUG
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/module.h>
|
|
#include <linux/virtio.h>
|
|
#include <linux/virtio_net.h>
|
|
#include <linux/bpf.h>
|
|
#include <linux/bpf_trace.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/average.h>
|
|
#include <linux/filter.h>
|
|
#include <linux/kernel.h>
|
|
#include <net/route.h>
|
|
#include <net/xdp.h>
|
|
#include <net/net_failover.h>
|
|
#include "crete_nic.h"
|
|
#include "crete_nic_io.h"
|
|
#include <linux/firmware.h>
|
|
#include "../crete-core/crete_fw_update.h"
|
|
|
|
#define CRETE_VNIC_DRIVER_VERSION "crete-vnic-1.0"
|
|
/******************net tools****************************/
|
|
static void crete_vnic_get_drvinfo(struct net_device *dev,
|
|
struct ethtool_drvinfo *info)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
struct crete_core_dev *core_dev = vnic_priv->coredev;
|
|
struct pci_dev *pdev = core_dev->pdev;
|
|
|
|
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
|
|
|
|
snprintf(info->fw_version, sizeof(info->fw_version),
|
|
"%d.%d.%d.%d", fw_ver_maj(core_dev),
|
|
fw_ver_min(core_dev), fw_ver_sub(core_dev), fw_ver_rev(core_dev));
|
|
|
|
strlcpy(info->version, CRETE_VNIC_DRIVER_VERSION,
|
|
sizeof(info->version));
|
|
strlcpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info));
|
|
}
|
|
|
|
static u32 crete_vnic_get_link(struct net_device *netdev)
|
|
{
|
|
//struct crete_vnic_priv *vnic_priv = netdev_priv(netdev);
|
|
//struct crete_core_dev *core_dev =vnic_priv->coredev;
|
|
/* If the link is not reported up to netdev, interrupts are disabled,
|
|
* and so the physical link state may have changed since we last
|
|
* looked. Set get_link_status to make sure that the true link
|
|
* state is interrogated, rather than pulling a cached and possibly
|
|
* stale link state from the driver.
|
|
*/
|
|
if (!netif_carrier_ok(netdev)) {
|
|
/*todo to vdev get link status */
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* about the anolis 5.10.134-14 kernel version
|
|
* auxliary define with return int value
|
|
*/
|
|
#if defined(SNIC_ANOLIS_VERSION14) || !defined(CONFIG_ETHTOOL_EXTEND_RINGPARAM)
|
|
static void crete_vnic_get_ringparam(struct net_device *dev,
|
|
struct ethtool_ringparam *ring)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
|
|
ring->rx_max_pending = virtqueue_get_vring_size(vnic_priv->rq[0].vq);
|
|
ring->tx_max_pending = virtqueue_get_vring_size(vnic_priv->sq[0].vq);
|
|
ring->rx_pending = ring->rx_max_pending;
|
|
ring->tx_pending = ring->tx_max_pending;
|
|
}
|
|
|
|
#else
|
|
|
|
static void crete_vnic_get_ringparam(struct net_device *dev,
|
|
struct ethtool_ringparam *ring,
|
|
struct kernel_ethtool_ringparam
|
|
*kernel_ring,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
|
|
ring->rx_max_pending = virtqueue_get_vring_size(vnic_priv->rq[0].vq);
|
|
ring->tx_max_pending = virtqueue_get_vring_size(vnic_priv->sq[0].vq);
|
|
ring->rx_pending = ring->rx_max_pending;
|
|
ring->tx_pending = ring->tx_max_pending;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void crete_vnic_get_channels(struct net_device *dev,
|
|
struct ethtool_channels *channels)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
|
|
channels->combined_count = vnic_priv->curr_queue_pairs;
|
|
channels->max_combined = vnic_priv->max_queue_pairs;
|
|
channels->max_other = 0;
|
|
channels->rx_count = 0;
|
|
channels->tx_count = 0;
|
|
channels->other_count = 0;
|
|
}
|
|
|
|
/* TODO: Eliminate OOO packets during switching */
|
|
static int crete_vnic_set_channels(struct net_device *dev,
|
|
struct ethtool_channels *channels)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
u16 queue_pairs = channels->combined_count;
|
|
int err;
|
|
|
|
/* We don't support separate rx/tx channels.
|
|
* We don't allow setting 'other' channels.
|
|
*/
|
|
if (channels->rx_count || channels->tx_count || channels->other_count)
|
|
return -EINVAL;
|
|
|
|
if (queue_pairs > vnic_priv->max_queue_pairs || queue_pairs == 0)
|
|
return -EINVAL;
|
|
|
|
err = __crete_vnic_set_queues(dev, queue_pairs);
|
|
if (err)
|
|
goto err;
|
|
|
|
netif_set_real_num_tx_queues(dev, queue_pairs);
|
|
netif_set_real_num_rx_queues(dev, queue_pairs);
|
|
err:
|
|
return err;
|
|
}
|
|
|
|
int crete_vnic_ethtool_flash_device(struct crete_vnic_priv *vnic_priv,
|
|
struct ethtool_flash *flash)
|
|
{
|
|
struct crete_core_dev *cdev = vnic_priv->coredev;
|
|
struct net_device *dev = vnic_priv->netdev;
|
|
const struct firmware *fw;
|
|
int err;
|
|
|
|
if (flash->region != ETHTOOL_FLASH_ALL_REGIONS)
|
|
return -EOPNOTSUPP;
|
|
|
|
err = request_firmware_direct(&fw, flash->data, &dev->dev);
|
|
if (err)
|
|
return err;
|
|
|
|
dev_hold(dev);
|
|
rtnl_unlock();
|
|
|
|
err = crete_firmware_flash(cdev, fw, NULL, CRETE_FWU_ETHTOOL);
|
|
release_firmware(fw);
|
|
|
|
rtnl_lock();
|
|
dev_put(dev);
|
|
return err;
|
|
}
|
|
|
|
static int crete_vnic_flash_device(struct net_device *dev,
|
|
struct ethtool_flash *flash)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
|
|
return crete_vnic_ethtool_flash_device(vnic_priv, flash);
|
|
}
|
|
|
|
struct ptys2ethtool_config {
|
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
|
|
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertised);
|
|
};
|
|
|
|
static
|
|
struct ptys2ethtool_config ptys2ethtool_table[CRETE_LINK_MODES_CAP_BIT];
|
|
|
|
#define CRETE_BUILD_PTYS2ETHTOOL_CONFIG(reg_, table, ...) \
|
|
({ \
|
|
struct ptys2ethtool_config *cfg; \
|
|
const unsigned int modes[] = { __VA_ARGS__ }; \
|
|
unsigned int i, bit, idx; \
|
|
cfg = &table[reg_]; \
|
|
bitmap_zero(cfg->supported, \
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS); \
|
|
bitmap_zero(cfg->advertised, \
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS); \
|
|
for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) { \
|
|
bit = modes[i] % 64; \
|
|
idx = modes[i] / 64; \
|
|
__set_bit(bit, &cfg->supported[idx]); \
|
|
__set_bit(bit, &cfg->advertised[idx]); \
|
|
} \
|
|
})
|
|
|
|
void crete_build_ptys2ethtool_map(void)
|
|
{
|
|
memset(ptys2ethtool_table, 0, sizeof(ptys2ethtool_table));
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_10GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
|
|
ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
|
|
ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
|
|
ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
|
|
ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
|
|
ETHTOOL_LINK_MODE_10000baseER_Full_BIT);
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_25GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
|
|
ETHTOOL_LINK_MODE_25000baseSR_Full_BIT);
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_40GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT);
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_50GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT);
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_100GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT);
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_200GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
|
|
ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT);
|
|
CRETE_BUILD_PTYS2ETHTOOL_CONFIG(CRETE_400GBASE_BIT, ptys2ethtool_table,
|
|
ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
|
|
ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT);
|
|
}
|
|
|
|
u16 crete_port_speed2linkmode(u32 speed)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(crete_link_speed); ++i) {
|
|
if (crete_link_speed[i] == speed)
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int crete_ethtool_set_link_ksettings(struct crete_vnic_priv *vnic_priv,
|
|
const struct ethtool_link_ksettings *link_ksettings)
|
|
{
|
|
struct crete_core_dev *cdev = vnic_priv->coredev;
|
|
const struct ethtool_link_settings *base = &link_ksettings->base;
|
|
struct net_device *dev = vnic_priv->netdev;
|
|
struct crete_speed_duplex sd_current = {0};
|
|
u16 change_bit = 0;
|
|
u16 link_mode;
|
|
int ret;
|
|
|
|
if (base->autoneg == AUTONEG_ENABLE) {
|
|
netdev_err(dev, "Autonegotiation is not supported!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (base->duplex != DUPLEX_FULL) {
|
|
netdev_err(dev, "Only full duplex mode is supported!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
link_mode = crete_port_speed2linkmode(base->speed);
|
|
if (link_mode >= CRETE_LINK_MODES_NUMBER || link_mode < CRETE_10GBASE) {
|
|
netdev_err(dev, "speed parameter is illeagl.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = crete_get_port_speed_duplex(cdev, &sd_current);
|
|
if (ret) {
|
|
netdev_err(dev, "Failed to get current speed and duplex mode!\n");
|
|
return ret;
|
|
}
|
|
|
|
if (sd_current.speed == link_mode && sd_current.duplex == base->duplex) {
|
|
netdev_info(dev, "Speed and duplex mode are already set to the requested values.\n");
|
|
return 0;
|
|
}
|
|
|
|
change_bit |= (sd_current.speed != link_mode) ? LINK_SPEED_OP_BIT : 0;
|
|
change_bit |= (sd_current.duplex != base->duplex) ? DUPLEX_MODE_OP_BIT : 0;
|
|
|
|
if (!netif_running(dev)) {
|
|
netdev_err(dev, "Network interface is not running. Cannot change settings.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = crete_set_port_speed_duplex(cdev, link_mode, base->duplex, change_bit);
|
|
if (ret) {
|
|
netdev_err(dev, "Failed to set speed and duplex mode!\n");
|
|
return ret;
|
|
}
|
|
|
|
netdev_info(dev, "Speed set to %d Mbps, duplex mode set to full.\n", base->speed);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int crete_vnic_set_link_ksettings(struct net_device *dev,
|
|
const struct ethtool_link_ksettings *link_ksettings)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
|
|
return crete_ethtool_set_link_ksettings(vnic_priv, link_ksettings);
|
|
}
|
|
|
|
static void ptys2ethtool_adver_link(u32 eth_speed_cap,
|
|
u8 tx_pause, u8 rx_pause, struct ethtool_link_ksettings *link_ksettings)
|
|
{
|
|
unsigned long speed_cap = eth_speed_cap;
|
|
unsigned long *advertising_modes = link_ksettings->link_modes.advertising;
|
|
int speed;
|
|
|
|
for_each_set_bit(speed, &speed_cap, CRETE_LINK_MODES_CAP_BIT)
|
|
bitmap_or(advertising_modes, advertising_modes,
|
|
ptys2ethtool_table[speed].advertised,
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
if (rx_pause)
|
|
ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, Pause);
|
|
if (tx_pause ^ rx_pause)
|
|
ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, Asym_Pause);
|
|
}
|
|
|
|
static void ptys2ethtool_supported_link(u32 eth_speed_cap,
|
|
struct ethtool_link_ksettings *link_ksettings)
|
|
{
|
|
unsigned long speed_cap = eth_speed_cap;
|
|
unsigned long *supported_modes = link_ksettings->link_modes.supported;
|
|
int speed;
|
|
|
|
for_each_set_bit(speed, &speed_cap, CRETE_LINK_MODES_CAP_BIT)
|
|
bitmap_or(supported_modes, supported_modes,
|
|
ptys2ethtool_table[speed].supported,
|
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Pause);
|
|
}
|
|
|
|
static int get_fec_supported_advertised(struct crete_core_dev *cdev,
|
|
struct ethtool_link_ksettings *link_ksettings)
|
|
{
|
|
u16 active_fec;
|
|
u16 eth_fec_cap;
|
|
u8 fec;
|
|
int err;
|
|
unsigned long fec_cap;
|
|
|
|
err = crete_get_fec_mode(cdev, &active_fec, ð_fec_cap);
|
|
if (err)
|
|
return err;
|
|
fec_cap = eth_fec_cap;
|
|
for_each_set_bit(fec, &fec_cap, ARRAY_SIZE(crete_fec_2_ethtool_bit))
|
|
__set_bit(crete_fec_2_ethtool_bit[fec], link_ksettings->link_modes.supported);
|
|
|
|
if (active_fec <= CRETE_FEC_LLRS)
|
|
__set_bit(crete_fec_2_ethtool_bit[active_fec],
|
|
link_ksettings->link_modes.advertising);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int crete_vnic_get_link_ksettings(struct net_device *dev,
|
|
struct ethtool_link_ksettings *link_ksettings)
|
|
{
|
|
struct crete_speed_duplex sd = {0};
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
struct crete_core_dev *cdev = vnic_priv->coredev;
|
|
u8 rx_pause = 0;
|
|
u8 tx_pause = 0;
|
|
int ret = 0;
|
|
|
|
link_ksettings->base.speed = SPEED_UNKNOWN;
|
|
link_ksettings->base.duplex = DUPLEX_UNKNOWN;
|
|
ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising);
|
|
ethtool_link_ksettings_zero_link_mode(link_ksettings, supported);
|
|
|
|
crete_get_pfc_enable(cdev, &rx_pause, &tx_pause);
|
|
|
|
ret = crete_get_port_speed_duplex(cdev, &sd);
|
|
if (ret) {
|
|
netdev_err(dev, "Failed to get speed and duplex mode!\n");
|
|
ret = 0;
|
|
}
|
|
if (sd.speed >= CRETE_10GBASE && sd.speed < CRETE_LINK_MODES_NUMBER)
|
|
link_ksettings->base.speed = crete_link_speed[sd.speed];
|
|
|
|
if (sd.duplex >= CRETE_HALF_DUPLEX && sd.duplex < CRETE_DUPLEX_MAX)
|
|
link_ksettings->base.duplex = crete_link_duplex[sd.duplex];
|
|
|
|
netdev_info(dev, "Speed Get speed[%d] speed_cap[0x%x].\n", sd.speed, sd.speed_cap);
|
|
ptys2ethtool_supported_link(sd.speed_cap, link_ksettings);
|
|
ptys2ethtool_adver_link(sd.speed_cap, tx_pause, rx_pause, link_ksettings);
|
|
link_ksettings->base.port = PORT_FIBRE;
|
|
|
|
ret = get_fec_supported_advertised(cdev, link_ksettings);
|
|
if (ret) {
|
|
netdev_err(dev, "%s: FEC caps query failed: %d\n",
|
|
__func__, ret);
|
|
ret = 0; /* don't fail caps query because of FEC error */
|
|
}
|
|
|
|
ethtool_link_ksettings_add_link_mode(link_ksettings, supported, FIBRE);
|
|
ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, FIBRE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int crete_vnic_set_fecparam(struct net_device *dev,
|
|
struct ethtool_fecparam *fecparam)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
struct crete_core_dev *cdev = vnic_priv->coredev;
|
|
|
|
unsigned long fec_bitmap;
|
|
u16 fec_policy = 0;
|
|
|
|
bitmap_from_arr32(&fec_bitmap, &fecparam->fec, sizeof(fecparam->fec) * BITS_PER_BYTE);
|
|
if (bitmap_weight(&fec_bitmap, ETHTOOL_FEC_LLRS_BIT + 1) > 1)
|
|
return -EOPNOTSUPP;
|
|
|
|
switch (fecparam->fec) {
|
|
case ETHTOOL_FEC_OFF:
|
|
fec_policy = CRETE_FEC_OFF;
|
|
break;
|
|
case ETHTOOL_FEC_RS:
|
|
fec_policy = CRETE_FEC_RS;
|
|
break;
|
|
case ETHTOOL_FEC_BASER:
|
|
fec_policy = CRETE_FEC_BASER;
|
|
break;
|
|
default:
|
|
netdev_err(dev, "Unsupported FEC mode: %d\n", fecparam->fec);
|
|
return -EINVAL;
|
|
}
|
|
return crete_set_fec_mode(cdev, fec_policy);
|
|
}
|
|
|
|
static u32 crete_fec_cap_to_ethtool_fec(u16 fec_cap)
|
|
{
|
|
u32 ethtool_fec = 0;
|
|
|
|
if (fec_cap & BIT(CRETE_FEC_OFF))
|
|
ethtool_fec |= ETHTOOL_FEC_OFF;
|
|
|
|
if (fec_cap & BIT(CRETE_FEC_AUTO))
|
|
ethtool_fec |= ETHTOOL_FEC_AUTO;
|
|
|
|
if (fec_cap & BIT(CRETE_FEC_RS))
|
|
ethtool_fec |= ETHTOOL_FEC_RS;
|
|
|
|
if (fec_cap & BIT(CRETE_FEC_BASER))
|
|
ethtool_fec |= ETHTOOL_FEC_BASER;
|
|
|
|
if (fec_cap & BIT(CRETE_FEC_LLRS))
|
|
ethtool_fec |= ETHTOOL_FEC_LLRS;
|
|
|
|
return ethtool_fec;
|
|
}
|
|
|
|
static int crete_vnic_get_fecparam(struct net_device *dev,
|
|
struct ethtool_fecparam *fecparam)
|
|
{
|
|
struct crete_vnic_priv *vnic_priv = netdev_priv(dev);
|
|
struct crete_core_dev *cdev = vnic_priv->coredev;
|
|
u16 fec_active;
|
|
u16 fec_cap;
|
|
int err;
|
|
|
|
err = crete_get_fec_mode(cdev, &fec_active, &fec_cap);
|
|
if (err)
|
|
return err;
|
|
|
|
fecparam->active_fec = crete_fec_2_ethtool[fec_active];
|
|
|
|
fecparam->fec = crete_fec_cap_to_ethtool_fec(fec_cap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct ethtool_ops crete_vnic_ethtool_ops = {
|
|
.get_drvinfo = crete_vnic_get_drvinfo,
|
|
.get_link = crete_vnic_get_link,
|
|
.get_ringparam = crete_vnic_get_ringparam,
|
|
.get_strings = crete_vnic_get_strings,
|
|
.get_sset_count = crete_vnic_get_sset_count,
|
|
.get_ethtool_stats = crete_vnic_get_ethtool_stats,
|
|
.get_channels = crete_vnic_get_channels,
|
|
.set_channels = crete_vnic_set_channels,
|
|
.flash_device = crete_vnic_flash_device,
|
|
.get_link_ksettings = crete_vnic_get_link_ksettings,
|
|
.set_link_ksettings = crete_vnic_set_link_ksettings,
|
|
.get_fecparam = crete_vnic_get_fecparam,
|
|
.set_fecparam = crete_vnic_set_fecparam,
|
|
};
|
|
|
|
void crete_set_ethtool_ops(struct net_device *netdev)
|
|
{
|
|
netdev->ethtool_ops = &crete_vnic_ethtool_ops;
|
|
}
|