1178 lines
39 KiB
C
Executable File
1178 lines
39 KiB
C
Executable File
/*++
|
|
|
|
Copyright (c) 2021 Motor-comm Corporation.
|
|
Confidential and Proprietary. All rights reserved.
|
|
|
|
This is Motor-comm Corporation NIC driver relevant files. Please don't copy, modify,
|
|
distribute without commercial permission.
|
|
|
|
--*/
|
|
|
|
|
|
#include <linux/ethtool.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/netdevice.h>
|
|
|
|
#include "fuxi-gmac.h"
|
|
#include "fuxi-gmac-reg.h"
|
|
|
|
struct fxgmac_stats_desc {
|
|
char stat_string[ETH_GSTRING_LEN];
|
|
int stat_offset;
|
|
};
|
|
|
|
#define FXGMAC_STAT(str, var) \
|
|
{ \
|
|
str, \
|
|
offsetof(struct fxgmac_pdata, stats.var), \
|
|
}
|
|
|
|
static const struct fxgmac_stats_desc fxgmac_gstring_stats[] = {
|
|
/* MMC TX counters */
|
|
FXGMAC_STAT("tx_bytes", txoctetcount_gb),
|
|
FXGMAC_STAT("tx_bytes_good", txoctetcount_g),
|
|
FXGMAC_STAT("tx_packets", txframecount_gb),
|
|
FXGMAC_STAT("tx_packets_good", txframecount_g),
|
|
FXGMAC_STAT("tx_unicast_packets", txunicastframes_gb),
|
|
FXGMAC_STAT("tx_broadcast_packets", txbroadcastframes_gb),
|
|
FXGMAC_STAT("tx_broadcast_packets_good", txbroadcastframes_g),
|
|
FXGMAC_STAT("tx_multicast_packets", txmulticastframes_gb),
|
|
FXGMAC_STAT("tx_multicast_packets_good", txmulticastframes_g),
|
|
FXGMAC_STAT("tx_vlan_packets_good", txvlanframes_g),
|
|
FXGMAC_STAT("tx_64_byte_packets", tx64octets_gb),
|
|
FXGMAC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb),
|
|
FXGMAC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb),
|
|
FXGMAC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb),
|
|
FXGMAC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb),
|
|
FXGMAC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb),
|
|
FXGMAC_STAT("tx_underflow_errors", txunderflowerror),
|
|
FXGMAC_STAT("tx_pause_frames", txpauseframes),
|
|
FXGMAC_STAT("tx_single_collision", txsinglecollision_g),
|
|
FXGMAC_STAT("tx_multiple_collision", txmultiplecollision_g),
|
|
FXGMAC_STAT("tx_deferred_frames", txdeferredframes),
|
|
FXGMAC_STAT("tx_late_collision_frames", txlatecollisionframes),
|
|
FXGMAC_STAT("tx_excessive_collision_frames", txexcessivecollisionframes),
|
|
FXGMAC_STAT("tx_carrier_error_frames", txcarriererrorframes),
|
|
FXGMAC_STAT("tx_excessive_deferral_error", txexcessivedeferralerror),
|
|
FXGMAC_STAT("tx_oversize_frames_good", txoversize_g),
|
|
|
|
/* MMC RX counters */
|
|
FXGMAC_STAT("rx_bytes", rxoctetcount_gb),
|
|
FXGMAC_STAT("rx_bytes_good", rxoctetcount_g),
|
|
FXGMAC_STAT("rx_packets", rxframecount_gb),
|
|
FXGMAC_STAT("rx_unicast_packets_good", rxunicastframes_g),
|
|
FXGMAC_STAT("rx_broadcast_packets_good", rxbroadcastframes_g),
|
|
FXGMAC_STAT("rx_multicast_packets_good", rxmulticastframes_g),
|
|
FXGMAC_STAT("rx_vlan_packets_mac", rxvlanframes_gb),
|
|
FXGMAC_STAT("rx_64_byte_packets", rx64octets_gb),
|
|
FXGMAC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb),
|
|
FXGMAC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb),
|
|
FXGMAC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb),
|
|
FXGMAC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb),
|
|
FXGMAC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb),
|
|
FXGMAC_STAT("rx_undersize_packets_good", rxundersize_g),
|
|
FXGMAC_STAT("rx_oversize_packets_good", rxoversize_g),
|
|
FXGMAC_STAT("rx_crc_errors", rxcrcerror),
|
|
FXGMAC_STAT("rx_align_error", rxalignerror),
|
|
FXGMAC_STAT("rx_crc_errors_small_packets", rxrunterror),
|
|
FXGMAC_STAT("rx_crc_errors_giant_packets", rxjabbererror),
|
|
FXGMAC_STAT("rx_length_errors", rxlengtherror),
|
|
FXGMAC_STAT("rx_out_of_range_errors", rxoutofrangetype),
|
|
FXGMAC_STAT("rx_fifo_overflow_errors", rxfifooverflow),
|
|
FXGMAC_STAT("rx_watchdog_errors", rxwatchdogerror),
|
|
FXGMAC_STAT("rx_pause_frames", rxpauseframes),
|
|
FXGMAC_STAT("rx_receive_error_frames", rxreceiveerrorframe),
|
|
FXGMAC_STAT("rx_control_frames_good", rxcontrolframe_g),
|
|
|
|
/* Extra counters */
|
|
FXGMAC_STAT("tx_tso_packets", tx_tso_packets),
|
|
FXGMAC_STAT("rx_split_header_packets", rx_split_header_packets),
|
|
FXGMAC_STAT("tx_process_stopped", tx_process_stopped),
|
|
FXGMAC_STAT("rx_process_stopped", rx_process_stopped),
|
|
FXGMAC_STAT("tx_buffer_unavailable", tx_buffer_unavailable),
|
|
FXGMAC_STAT("rx_buffer_unavailable", rx_buffer_unavailable),
|
|
FXGMAC_STAT("fatal_bus_error", fatal_bus_error),
|
|
FXGMAC_STAT("tx_vlan_packets_net", tx_vlan_packets),
|
|
FXGMAC_STAT("rx_vlan_packets_net", rx_vlan_packets),
|
|
FXGMAC_STAT("napi_poll_isr", napi_poll_isr),
|
|
FXGMAC_STAT("napi_poll_txtimer", napi_poll_txtimer),
|
|
FXGMAC_STAT("alive_cnt_txtimer", cnt_alive_txtimer),
|
|
|
|
FXGMAC_STAT("ephy_poll_timer", ephy_poll_timer_cnt),
|
|
FXGMAC_STAT("mgmt_int_isr", mgmt_int_isr),
|
|
};
|
|
|
|
#define FXGMAC_STATS_COUNT ARRAY_SIZE(fxgmac_gstring_stats)
|
|
|
|
static void fxgmac_ethtool_get_drvinfo(struct net_device *netdev,
|
|
struct ethtool_drvinfo *drvinfo)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
u32 ver = pdata->hw_feat.version;
|
|
u32 sver, devid, userver;
|
|
|
|
strlcpy(drvinfo->driver, pdata->drv_name, sizeof(drvinfo->driver));
|
|
strlcpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version));
|
|
strlcpy(drvinfo->bus_info, dev_name(pdata->dev),
|
|
sizeof(drvinfo->bus_info));
|
|
/*
|
|
* D|DEVID: Indicates the Device family
|
|
* U|USERVER: User-defined Version
|
|
*/
|
|
sver = FXGMAC_GET_REG_BITS(ver, MAC_VR_SVER_POS,
|
|
MAC_VR_SVER_LEN);
|
|
devid = FXGMAC_GET_REG_BITS(ver, MAC_VR_DEVID_POS,
|
|
MAC_VR_DEVID_LEN);
|
|
userver = FXGMAC_GET_REG_BITS(ver, MAC_VR_USERVER_POS,
|
|
MAC_VR_USERVER_LEN);
|
|
/*DPRINTK("xlgma: No userver (%x) here, sver (%x) should be 0x51\n", userver, sver);*/
|
|
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
|
|
"S.D.U: %x.%x.%x", sver, devid, userver);
|
|
}
|
|
|
|
static u32 fxgmac_ethtool_get_msglevel(struct net_device *netdev)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
return pdata->msg_enable;
|
|
}
|
|
|
|
static void fxgmac_ethtool_set_msglevel(struct net_device *netdev,
|
|
u32 msglevel)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
DPRINTK("fxmac, set msglvl from %08x to %08x\n", pdata->msg_enable, msglevel);
|
|
pdata->msg_enable = msglevel;
|
|
}
|
|
|
|
static void fxgmac_ethtool_get_channels(struct net_device *netdev,
|
|
struct ethtool_channels *channel)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
#if (FXGMAC_RSS_FEATURE_ENABLED)
|
|
/* report maximum channels */
|
|
channel->max_combined = FXGMAC_MAX_DMA_CHANNELS;
|
|
channel->max_other = 0;
|
|
channel->other_count = 0;
|
|
|
|
/* record RSS queues */
|
|
channel->combined_count = FXGMAC_MAX_DMA_CHANNELS;
|
|
|
|
/* nothing else to report if RSS is disabled */
|
|
if (channel->combined_count == 1)
|
|
return;
|
|
DPRINTK("fxmac rss, get channels max=(combined %d,other %d),count(combined %d,other %d)\n", channel->max_combined, channel->max_other, channel->combined_count, channel->other_count);
|
|
#endif
|
|
|
|
channel->max_rx = FXGMAC_MAX_DMA_CHANNELS;
|
|
channel->max_tx = FXGMAC_MAX_DMA_CHANNELS;
|
|
channel->rx_count = pdata->rx_q_count;
|
|
channel->tx_count = pdata->tx_q_count;
|
|
DPRINTK("fxmac, get channels max=(rx %d,tx %d),count(%d,%d)\n", channel->max_rx, channel->max_tx, channel->rx_count, channel->tx_count);
|
|
}
|
|
|
|
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) )
|
|
static int fxgmac_ethtool_get_coalesce(struct net_device *netdev,
|
|
struct ethtool_coalesce *ec,
|
|
struct kernel_ethtool_coalesce *kernel_coal,
|
|
struct netlink_ext_ack *extack)
|
|
#else
|
|
static int fxgmac_ethtool_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec)
|
|
#endif
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
memset(ec, 0, sizeof(struct ethtool_coalesce));
|
|
ec->rx_coalesce_usecs = pdata->rx_usecs;
|
|
ec->tx_coalesce_usecs = pdata->tx_usecs;
|
|
/*If we need to assign values to other members,
|
|
* we need to modify the supported_coalesce_params of fxgmac_ethtool_ops synchronously
|
|
*/
|
|
//ec->rx_max_coalesced_frames = pdata->rx_frames;
|
|
//ec->tx_max_coalesced_frames = pdata->tx_frames;
|
|
|
|
DPRINTK("fxmac, get coalesce\n");
|
|
return 0;
|
|
}
|
|
|
|
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) )
|
|
static int fxgmac_ethtool_set_coalesce(struct net_device *netdev,
|
|
struct ethtool_coalesce *ec,
|
|
struct kernel_ethtool_coalesce *kernel_coal,
|
|
struct netlink_ext_ack *extack)
|
|
#else
|
|
static int fxgmac_ethtool_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec)
|
|
#endif
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
unsigned int rx_frames, rx_riwt, rx_usecs;
|
|
unsigned int tx_frames;
|
|
|
|
/* Check for not supported parameters */
|
|
if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) ||
|
|
(ec->tx_coalesce_usecs_high) ||
|
|
(ec->tx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) ||
|
|
(ec->stats_block_coalesce_usecs) || (ec->pkt_rate_low) ||
|
|
(ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) ||
|
|
(ec->rx_max_coalesced_frames_low) || (ec->rx_coalesce_usecs_low) ||
|
|
(ec->tx_coalesce_usecs_low) || (ec->tx_max_coalesced_frames_low) ||
|
|
(ec->pkt_rate_high) || (ec->rx_coalesce_usecs_high) ||
|
|
(ec->rx_max_coalesced_frames_high) ||
|
|
(ec->tx_max_coalesced_frames_high) ||
|
|
(ec->rate_sample_interval))
|
|
return -EOPNOTSUPP;
|
|
|
|
rx_usecs = ec->rx_coalesce_usecs;
|
|
rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs);
|
|
rx_frames = ec->rx_max_coalesced_frames;
|
|
tx_frames = ec->tx_max_coalesced_frames;
|
|
|
|
if ((rx_riwt > FXGMAC_MAX_DMA_RIWT) ||
|
|
(rx_riwt < FXGMAC_MIN_DMA_RIWT) ||
|
|
(rx_frames > pdata->rx_desc_count))
|
|
return -EINVAL;
|
|
|
|
if (tx_frames > pdata->tx_desc_count)
|
|
return -EINVAL;
|
|
|
|
pdata->rx_riwt = rx_riwt;
|
|
pdata->rx_usecs = rx_usecs;
|
|
pdata->rx_frames = rx_frames;
|
|
hw_ops->config_rx_coalesce(pdata);
|
|
|
|
pdata->tx_frames = tx_frames;
|
|
hw_ops->config_tx_coalesce(pdata);
|
|
|
|
pdata->tx_usecs = ec->tx_coalesce_usecs;
|
|
hw_ops->set_interrupt_moderation(pdata);
|
|
|
|
DPRINTK("fxmac, set coalesce\n");
|
|
return 0;
|
|
}
|
|
|
|
#if (FXGMAC_RSS_FEATURE_ENABLED)
|
|
static u32 fxgmac_get_rxfh_key_size(struct net_device *netdev)
|
|
{
|
|
return FXGMAC_RSS_HASH_KEY_SIZE;
|
|
}
|
|
|
|
static u32 fxgmac_rss_indir_size(struct net_device *netdev)
|
|
{
|
|
return FXGMAC_RSS_MAX_TABLE_SIZE;
|
|
}
|
|
|
|
static void fxgmac_get_reta(struct fxgmac_pdata *pdata, u32 *indir)
|
|
{
|
|
int i, reta_size = FXGMAC_RSS_MAX_TABLE_SIZE;
|
|
u16 rss_m;
|
|
#ifdef FXGMAC_ONE_CHANNLE
|
|
rss_m = FXGMAC_MAX_DMA_CHANNELS;
|
|
#else
|
|
rss_m = FXGMAC_MAX_DMA_CHANNELS - 1; //mask for index of channel, 0-3
|
|
#endif
|
|
|
|
for (i = 0; i < reta_size; i++)
|
|
indir[i] = pdata->rss_table[i] & rss_m;
|
|
}
|
|
|
|
static int fxgmac_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
|
|
u8 *hfunc)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
/*
|
|
ETH_RSS_HASH_TOP __ETH_RSS_HASH(TOP)
|
|
ETH_RSS_HASH_XOR __ETH_RSS_HASH(XOR)
|
|
ETH_RSS_HASH_CRC32 __ETH_RSS_HASH(CRC32)
|
|
*/
|
|
if (hfunc)
|
|
{
|
|
//*hfunc = ETH_RSS_HASH_XOR;
|
|
*hfunc = ETH_RSS_HASH_TOP;
|
|
DPRINTK("fxmac, get_rxfh for hash function\n");
|
|
}
|
|
|
|
if (indir)
|
|
{
|
|
fxgmac_get_reta(pdata, indir);
|
|
DPRINTK("fxmac, get_rxfh for indirection tab\n");
|
|
}
|
|
|
|
if (key)
|
|
{
|
|
memcpy(key, pdata->rss_key, fxgmac_get_rxfh_key_size(netdev));
|
|
DPRINTK("fxmac, get_rxfh for hash key\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fxgmac_set_rxfh(struct net_device *netdev, const u32 *indir,
|
|
const u8 *key, const u8 hfunc)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
int i;
|
|
u32 reta_entries = fxgmac_rss_indir_size(netdev);
|
|
int max_queues = FXGMAC_MAX_DMA_CHANNELS;
|
|
|
|
DPRINTK("fxmac, set_rxfh callin, indir=%lx, key=%lx, func=%02x\n", (unsigned long)indir, (unsigned long)key, hfunc);
|
|
|
|
if (hfunc)
|
|
return -EINVAL;
|
|
|
|
/* Fill out the redirection table */
|
|
if (indir) {
|
|
#if FXGMAC_MSIX_CH0RXDIS_EN
|
|
max_queues = max_queues; // kill warning
|
|
reta_entries = reta_entries;
|
|
i = i;
|
|
DPRINTK("fxmac, set_rxfh, change of indirect talbe is not supported.\n");
|
|
return -EINVAL;
|
|
#else
|
|
/* double check user input. */
|
|
for (i = 0; i < reta_entries; i++)
|
|
if (indir[i] >= max_queues)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < reta_entries; i++)
|
|
pdata->rss_table[i] = indir[i];
|
|
|
|
hw_ops->write_rss_lookup_table(pdata);
|
|
#endif
|
|
}
|
|
|
|
/* Fill out the rss hash key */
|
|
if (FXGMAC_RSS_HASH_KEY_LINUX && key) {
|
|
hw_ops->set_rss_hash_key(pdata, key);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fxgmac_get_rss_hash_opts(struct fxgmac_pdata *pdata,
|
|
struct ethtool_rxnfc *cmd)
|
|
{
|
|
u32 reg_opt;
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
cmd->data = 0;
|
|
|
|
reg_opt = hw_ops->get_rss_options(pdata);
|
|
DPRINTK ("fxgmac_get_rss_hash_opts, hw=%02x, %02x\n", reg_opt, pdata->rss_options);
|
|
|
|
if(reg_opt != pdata->rss_options)
|
|
{
|
|
DPRINTK ("fxgmac_get_rss_hash_opts, warning, options are not consistent\n");
|
|
}
|
|
|
|
/* Report default options for RSS */
|
|
switch (cmd->flow_type) {
|
|
case TCP_V4_FLOW:
|
|
case UDP_V4_FLOW:
|
|
if(((TCP_V4_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_TCP4TE_POS, MAC_RSSCR_TCP4TE_LEN))) ||
|
|
((UDP_V4_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_UDP4TE_POS, MAC_RSSCR_UDP4TE_LEN))))
|
|
{
|
|
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
}
|
|
fallthrough;
|
|
case SCTP_V4_FLOW:
|
|
case AH_ESP_V4_FLOW:
|
|
case AH_V4_FLOW:
|
|
case ESP_V4_FLOW:
|
|
case IPV4_FLOW:
|
|
if(((TCP_V4_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_TCP4TE_POS, MAC_RSSCR_TCP4TE_LEN))) ||
|
|
((UDP_V4_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_UDP4TE_POS, MAC_RSSCR_UDP4TE_LEN))) ||
|
|
(FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_IP4TE_POS, MAC_RSSCR_IP4TE_LEN)))
|
|
{
|
|
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
|
|
}
|
|
break;
|
|
case TCP_V6_FLOW:
|
|
case UDP_V6_FLOW:
|
|
if(((TCP_V6_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_TCP6TE_POS, MAC_RSSCR_TCP6TE_LEN))) ||
|
|
((UDP_V6_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_UDP6TE_POS, MAC_RSSCR_UDP6TE_LEN))))
|
|
{
|
|
cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
|
|
}
|
|
fallthrough;
|
|
case SCTP_V6_FLOW:
|
|
case AH_ESP_V6_FLOW:
|
|
case AH_V6_FLOW:
|
|
case ESP_V6_FLOW:
|
|
case IPV6_FLOW:
|
|
if(((TCP_V6_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_TCP6TE_POS, MAC_RSSCR_TCP6TE_LEN))) ||
|
|
((UDP_V6_FLOW == (cmd->flow_type)) && (FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_UDP6TE_POS, MAC_RSSCR_UDP6TE_LEN))) ||
|
|
(FXGMAC_GET_REG_BITS(pdata->rss_options, MAC_RSSCR_IP6TE_POS, MAC_RSSCR_IP6TE_LEN)))
|
|
{
|
|
cmd->data |= RXH_IP_SRC | RXH_IP_DST;
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fxgmac_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
|
|
u32 *rule_locs)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(dev);
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
switch (cmd->cmd) {
|
|
case ETHTOOL_GRXRINGS:
|
|
cmd->data = pdata->rx_q_count;
|
|
ret = 0;
|
|
DPRINTK("fxmac, get_rxnfc for rx ring cnt\n");
|
|
break;
|
|
case ETHTOOL_GRXCLSRLCNT:
|
|
cmd->rule_cnt = 0;
|
|
ret = 0;
|
|
DPRINTK("fxmac, get_rxnfc for classify rule cnt\n");
|
|
break;
|
|
case ETHTOOL_GRXCLSRULE:
|
|
DPRINTK("fxmac, get_rxnfc for classify rules\n");
|
|
ret = 0;//ixgbe_get_ethtool_fdir_entry(adapter, cmd);
|
|
break;
|
|
case ETHTOOL_GRXCLSRLALL:
|
|
cmd->rule_cnt = 0;
|
|
ret = 0;
|
|
/*ret = ixgbe_get_ethtool_fdir_all(adapter, cmd,
|
|
(u32 *)rule_locs);
|
|
*/
|
|
DPRINTK("fxmac, get_rxnfc for classify both cnt and rules\n");
|
|
break;
|
|
case ETHTOOL_GRXFH:
|
|
ret = fxgmac_get_rss_hash_opts(pdata, cmd);
|
|
DPRINTK("fxmac, get_rxnfc for hash options\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define UDP_RSS_FLAGS (BIT(MAC_RSSCR_UDP4TE_POS) | \
|
|
BIT(MAC_RSSCR_UDP6TE_POS))
|
|
static int fxgmac_set_rss_hash_opt(struct fxgmac_pdata *pdata,
|
|
struct ethtool_rxnfc *nfc)
|
|
{
|
|
u32 rssopt = 0; //pdata->rss_options;
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
DPRINTK("fxgmac_set_rss_hash_opt call in,nfc_data=%llx,cur opt=%x\n", nfc->data, pdata->rss_options);
|
|
|
|
/*
|
|
* For RSS, it does not support anything other than hashing
|
|
* to queues on src,dst IPs and L4 ports
|
|
*/
|
|
if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST |
|
|
RXH_L4_B_0_1 | RXH_L4_B_2_3))
|
|
return -EINVAL;
|
|
|
|
switch (nfc->flow_type) {
|
|
case TCP_V4_FLOW:
|
|
case TCP_V6_FLOW:
|
|
/* default to TCP flow and do nothting */
|
|
if (!(nfc->data & RXH_IP_SRC) ||
|
|
!(nfc->data & RXH_IP_DST) ||
|
|
!(nfc->data & RXH_L4_B_0_1) ||
|
|
!(nfc->data & RXH_L4_B_2_3))
|
|
return -EINVAL;
|
|
if(TCP_V4_FLOW == (nfc->flow_type))
|
|
{
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_IP4TE_POS,
|
|
MAC_RSSCR_IP4TE_LEN, 1);
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_TCP4TE_POS,
|
|
MAC_RSSCR_TCP4TE_LEN, 1);
|
|
}
|
|
|
|
if(TCP_V6_FLOW == (nfc->flow_type))
|
|
{
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_IP6TE_POS,
|
|
MAC_RSSCR_IP6TE_LEN, 1);
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_TCP6TE_POS,
|
|
MAC_RSSCR_TCP6TE_LEN, 1);
|
|
}
|
|
break;
|
|
|
|
case UDP_V4_FLOW:
|
|
if (!(nfc->data & RXH_IP_SRC) ||
|
|
!(nfc->data & RXH_IP_DST))
|
|
return -EINVAL;
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_IP4TE_POS,
|
|
MAC_RSSCR_IP4TE_LEN, 1);
|
|
switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
|
|
case 0:
|
|
break;
|
|
case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_UDP4TE_POS,
|
|
MAC_RSSCR_UDP4TE_LEN, 1);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case UDP_V6_FLOW:
|
|
if (!(nfc->data & RXH_IP_SRC) ||
|
|
!(nfc->data & RXH_IP_DST))
|
|
return -EINVAL;
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_IP6TE_POS,
|
|
MAC_RSSCR_IP6TE_LEN, 1);
|
|
|
|
switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
|
|
case 0:
|
|
break;
|
|
case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
|
|
rssopt = FXGMAC_SET_REG_BITS(
|
|
rssopt,
|
|
MAC_RSSCR_UDP6TE_POS,
|
|
MAC_RSSCR_UDP6TE_LEN, 1);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case AH_ESP_V4_FLOW:
|
|
case AH_V4_FLOW:
|
|
case ESP_V4_FLOW:
|
|
case SCTP_V4_FLOW:
|
|
case AH_ESP_V6_FLOW:
|
|
case AH_V6_FLOW:
|
|
case ESP_V6_FLOW:
|
|
case SCTP_V6_FLOW:
|
|
if (!(nfc->data & RXH_IP_SRC) ||
|
|
!(nfc->data & RXH_IP_DST) ||
|
|
(nfc->data & RXH_L4_B_0_1) ||
|
|
(nfc->data & RXH_L4_B_2_3))
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* if options are changed, then update to hw */
|
|
if (rssopt != pdata->rss_options) {
|
|
if ((rssopt & UDP_RSS_FLAGS) &&
|
|
!(pdata->rss_options & UDP_RSS_FLAGS))
|
|
DPRINTK("enabling UDP RSS: fragmented packets"
|
|
" may arrive out of order to the stack above\n");
|
|
|
|
DPRINTK("rss option changed from %x to %x\n", pdata->rss_options, rssopt);
|
|
pdata->rss_options = rssopt;
|
|
#if 1
|
|
hw_ops->set_rss_options(pdata);
|
|
#else
|
|
/* Perform hash on these packet types */
|
|
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4
|
|
| IXGBE_MRQC_RSS_FIELD_IPV4_TCP
|
|
| IXGBE_MRQC_RSS_FIELD_IPV6
|
|
| IXGBE_MRQC_RSS_FIELD_IPV6_TCP;
|
|
|
|
mrqc &= ~(IXGBE_MRQC_RSS_FIELD_IPV4_UDP |
|
|
IXGBE_MRQC_RSS_FIELD_IPV6_UDP);
|
|
|
|
if (flags2 & IXGBE_FLAG2_RSS_FIELD_IPV4_UDP)
|
|
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4_UDP;
|
|
|
|
if (flags2 & IXGBE_FLAG2_RSS_FIELD_IPV6_UDP)
|
|
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_UDP;
|
|
|
|
if ((hw->mac.type >= ixgbe_mac_X550) &&
|
|
(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED))
|
|
IXGBE_WRITE_REG(hw, IXGBE_PFVFMRQC(pf_pool), mrqc);
|
|
else
|
|
IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fxgmac_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(dev);
|
|
|
|
int ret = -EOPNOTSUPP;
|
|
|
|
switch (cmd->cmd) {
|
|
case ETHTOOL_SRXCLSRLINS:
|
|
//no support. rx classifier rule insert
|
|
//ret = ixgbe_add_ethtool_fdir_entry(adapter, cmd);
|
|
DPRINTK("set_rxnfc for rx cls rule insert-n\\a\n");
|
|
break;
|
|
case ETHTOOL_SRXCLSRLDEL:
|
|
//no support. rx classifier rule delete
|
|
//ret = ixgbe_del_ethtool_fdir_entry(adapter, cmd);
|
|
DPRINTK("set_rxnfc for rx cls rule del-n\\a\n");
|
|
break;
|
|
case ETHTOOL_SRXFH:
|
|
DPRINTK("set_rxnfc for rx rss option\n");
|
|
ret = fxgmac_set_rss_hash_opt(pdata, cmd);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif //FXGMAC_RSS_FEATURE_ENABLED
|
|
|
|
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0) )
|
|
static void fxgmac_get_ringparam(struct net_device *netdev,
|
|
struct ethtool_ringparam *ring,
|
|
struct kernel_ethtool_ringparam *kernel_ring,
|
|
struct netlink_ext_ack *exact)
|
|
|
|
#else
|
|
static void fxgmac_get_ringparam(struct net_device *netdev,
|
|
struct ethtool_ringparam *ring)
|
|
#endif
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
DPRINTK("fxmac, get_ringparam callin\n");
|
|
|
|
ring->rx_max_pending = FXGMAC_RX_DESC_CNT;
|
|
ring->tx_max_pending = FXGMAC_TX_DESC_CNT;
|
|
ring->rx_mini_max_pending = 0;
|
|
ring->rx_jumbo_max_pending = 0;
|
|
ring->rx_pending = pdata->rx_desc_count;
|
|
ring->tx_pending = pdata->tx_desc_count;
|
|
ring->rx_mini_pending = 0;
|
|
ring->rx_jumbo_pending = 0;
|
|
}
|
|
|
|
#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0) )
|
|
static int fxgmac_set_ringparam(struct net_device *netdev,
|
|
struct ethtool_ringparam *ring,
|
|
struct kernel_ethtool_ringparam *kernel_ring,
|
|
struct netlink_ext_ack *exact)
|
|
|
|
#else
|
|
static int fxgmac_set_ringparam(struct net_device *netdev,
|
|
struct ethtool_ringparam *ring)
|
|
#endif
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops;
|
|
|
|
DPRINTK("fxmac, set_ringparam callin\n");
|
|
|
|
pdata->tx_desc_count = ring->tx_pending;
|
|
pdata->rx_desc_count = ring->rx_pending;
|
|
|
|
fxgmac_stop(pdata);
|
|
fxgmac_free_tx_data(pdata);
|
|
fxgmac_free_rx_data(pdata);
|
|
desc_ops->alloc_channles_and_rings(pdata);
|
|
fxgmac_start(pdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if FXGMAC_WOL_FEATURE_ENABLED
|
|
static void fxgmac_get_wol(struct net_device *netdev,
|
|
struct ethtool_wolinfo *wol)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
/* for further feature implementation
|
|
wol->supported = WAKE_PHY | WAKE_UCAST | WAKE_MCAST |
|
|
WAKE_BCAST | WAKE_MAGIC;
|
|
*/
|
|
|
|
wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC | WAKE_ARP;
|
|
#if FXGMAC_WOL_UPON_EPHY_LINK
|
|
wol->supported |= WAKE_PHY;
|
|
#endif
|
|
|
|
wol->wolopts = 0;
|
|
if (!(pdata->hw_feat.rwk) || !device_can_wakeup(/*pci_dev_to_dev*/(pdata->dev))) {
|
|
DPRINTK("fxgmac get_wol, pci does not support wakeup\n");
|
|
return;
|
|
}
|
|
#if 0
|
|
wol->wolopts |= WAKE_UCAST;
|
|
wol->wolopts |= WAKE_MCAST;
|
|
wol->wolopts |= WAKE_BCAST;
|
|
wol->wolopts |= WAKE_MAGIC;
|
|
#else
|
|
wol->wolopts = pdata->expansion.wol;
|
|
#endif
|
|
DPRINTK("fxmac, get_wol, 0x%x, 0x%x\n", wol->wolopts, pdata->expansion.wol);
|
|
}
|
|
|
|
static int fxgmac_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
//currently, we do not support these options
|
|
#if FXGMAC_WOL_UPON_EPHY_LINK
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0))
|
|
if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_FILTER)) {
|
|
#else
|
|
if (wol->wolopts & WAKE_MAGICSECURE) {
|
|
#endif
|
|
#else
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0))
|
|
if (wol->wolopts & (WAKE_PHY | WAKE_MAGICSECURE | WAKE_FILTER)) {
|
|
#else
|
|
if (wol->wolopts & (WAKE_PHY | WAKE_MAGICSECURE)) {
|
|
#endif
|
|
#endif
|
|
DPRINTK("fxmac, set_wol, not supported wol options, 0x%x\n", wol->wolopts);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (!(pdata->hw_feat.rwk)) {
|
|
DPRINTK("fxmac, set_wol, hw wol feature is n/a\n");
|
|
return (wol->wolopts ? -EOPNOTSUPP : 0);
|
|
}
|
|
|
|
#if 1 /* normally, pls set this to 1. */
|
|
pdata->expansion.wol = 0;
|
|
if (wol->wolopts & WAKE_UCAST)
|
|
pdata->expansion.wol |= WAKE_UCAST;
|
|
|
|
if (wol->wolopts & WAKE_MCAST)
|
|
pdata->expansion.wol |= WAKE_MCAST;
|
|
|
|
if (wol->wolopts & WAKE_BCAST)
|
|
pdata->expansion.wol |= WAKE_BCAST;
|
|
|
|
if (wol->wolopts & WAKE_MAGIC)
|
|
pdata->expansion.wol |= WAKE_MAGIC;
|
|
|
|
if (wol->wolopts & WAKE_PHY)
|
|
pdata->expansion.wol |= WAKE_PHY;
|
|
|
|
if (wol->wolopts & WAKE_ARP)
|
|
pdata->expansion.wol |= WAKE_ARP;
|
|
|
|
hw_ops->set_pattern_data(pdata);
|
|
|
|
hw_ops->config_wol(pdata, (!!(pdata->expansion.wol)));
|
|
#else
|
|
|
|
/* for test only... */
|
|
if (wol->wolopts & WAKE_UCAST) {
|
|
DPRINTK("fxmac, set_wol, ucast\n");
|
|
//fxgmac_net_powerdown(pdata, pdata->wol);
|
|
//fxgmac_update_aoe_ipv4addr(pdata, (u8 *)NULL);
|
|
//fxgmac_get_netdev_ip6addr(pdata, NULL, NULL);
|
|
fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_LOCAL_LINK);
|
|
}
|
|
|
|
if (wol->wolopts & WAKE_MAGIC) {
|
|
DPRINTK("fxmac, set_wol, magic\n");
|
|
//fxgmac_net_powerup(pdata);
|
|
//fxgmac_update_aoe_ipv4addr(pdata, (u8 *)"192.168.3.33");
|
|
fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_GLOBAL_UNICAST);
|
|
}
|
|
/*
|
|
if (wol->wolopts & WAKE_BCAST) {
|
|
extern int fxgmac_disable_pci_msi_config(struct pci_dev *pdev);
|
|
DPRINTK("fxmac, set_wol, broadcast\n");
|
|
fxgmac_disable_pci_msi_config(struct pci_dev *pdev);
|
|
}
|
|
*/
|
|
#endif
|
|
DPRINTK("fxmac, set_wol, opt=0x%x, 0x%x\n", wol->wolopts, pdata->expansion.wol);
|
|
|
|
return 0;
|
|
}
|
|
#endif /*FXGMAC_WOL_FEATURE_ENABLED*/
|
|
|
|
static int fxgmac_get_regs_len(struct net_device __always_unused *netdev)
|
|
{
|
|
return FXGMAC_EPHY_REGS_LEN * sizeof(u32);
|
|
}
|
|
|
|
static void fxgmac_get_regs(struct net_device *netdev, struct ethtool_regs *regs,
|
|
void *p)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
u32 *regs_buff = p;
|
|
u8 i;
|
|
|
|
memset(p, 0, FXGMAC_EPHY_REGS_LEN * sizeof(u32));
|
|
for (i = REG_MII_BMCR; i < FXGMAC_EPHY_REGS_LEN; i++) {
|
|
hw_ops->read_ephy_reg(pdata, i, (unsigned int *)®s_buff[i]);
|
|
}
|
|
regs->version = regs_buff[REG_MII_PHYSID1] << 16 | regs_buff[REG_MII_PHYSID2];
|
|
|
|
//fxgmac_ephy_soft_reset(pdata);
|
|
}
|
|
|
|
#if FXGMAC_PAUSE_FEATURE_ENABLED
|
|
static int fxgmac_get_link_ksettings(struct net_device *netdev,
|
|
struct ethtool_link_ksettings *cmd)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
u32 duplex, regval, link_status;
|
|
u32 adv = 0xFFFFFFFF;
|
|
|
|
regval = fxgmac_ephy_autoneg_ability_get(pdata, &adv);
|
|
if (regval)
|
|
return -ETIMEDOUT;
|
|
|
|
ethtool_link_ksettings_zero_link_mode(cmd, supported);
|
|
ethtool_link_ksettings_zero_link_mode(cmd, advertising);
|
|
|
|
/* set the supported link speeds */
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Full);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Half);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
|
|
|
|
/* Indicate pause support */
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause);
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, Asym_Pause);
|
|
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, MII);
|
|
cmd->base.port = PORT_MII;
|
|
|
|
ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
|
|
hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, ®val);
|
|
regval = FXGMAC_GET_REG_BITS(regval, PHY_CR_AUTOENG_POS, PHY_CR_AUTOENG_LEN);
|
|
if (regval) {
|
|
if (pdata->phy_autoeng)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
|
|
else
|
|
clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, cmd->link_modes.advertising);
|
|
|
|
if (adv & FXGMAC_ADVERTISE_10HALF)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
|
|
if (adv & FXGMAC_ADVERTISE_10FULL)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
|
|
if (adv & FXGMAC_ADVERTISE_100HALF)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
|
|
if (adv & FXGMAC_ADVERTISE_100FULL)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
|
|
if (adv & FXGMAC_ADVERTISE_1000FULL)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
|
|
}
|
|
else {
|
|
clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, cmd->link_modes.advertising);
|
|
switch (pdata->phy_speed) {
|
|
case SPEED_1000M:
|
|
if (pdata->phy_duplex)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
|
|
else
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Half);
|
|
break;
|
|
case SPEED_100M:
|
|
if (pdata->phy_duplex)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
|
|
else
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
|
|
break;
|
|
case SPEED_10M:
|
|
if (pdata->phy_duplex)
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
|
|
else
|
|
ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
cmd->base.autoneg = pdata->phy_autoeng ? regval : 0;
|
|
|
|
hw_ops->read_ephy_reg(pdata, REG_MII_SPEC_STATUS, ®val);
|
|
link_status = regval & (BIT(FUXI_EPHY_LINK_STATUS_BIT));
|
|
if (link_status) {
|
|
duplex = FXGMAC_GET_REG_BITS(regval, PHY_MII_SPEC_DUPLEX_POS, PHY_MII_SPEC_DUPLEX_LEN);
|
|
cmd->base.duplex = duplex;
|
|
cmd->base.speed = pdata->phy_speed;
|
|
}else {
|
|
cmd->base.duplex = DUPLEX_UNKNOWN;
|
|
cmd->base.speed = SPEED_UNKNOWN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fxgmac_set_link_ksettings(struct net_device *netdev,
|
|
const struct ethtool_link_ksettings *cmd)
|
|
{
|
|
u32 advertising, support, adv;
|
|
int ret;
|
|
struct fxphy_ag_adv;
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
|
|
if (cmd->base.speed == SPEED_1000 && cmd->base.duplex == DUPLEX_HALF)
|
|
return -EINVAL;
|
|
|
|
pdata->phy_autoeng = cmd->base.autoneg;
|
|
|
|
ethtool_convert_link_mode_to_legacy_u32(&advertising, cmd->link_modes.advertising);
|
|
ethtool_convert_link_mode_to_legacy_u32(&support, cmd->link_modes.supported);
|
|
advertising &= support;
|
|
|
|
if (pdata->phy_autoeng || (!pdata->phy_autoeng && cmd->base.speed == SPEED_1000)){
|
|
ret = hw_ops->read_ephy_reg(pdata, REG_MII_ADVERTISE, &adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
adv &= ~REG_BIT_ADVERTISE_100_10_CAP;
|
|
adv |= ethtool_adv_to_mii_adv_t(advertising);
|
|
ret = hw_ops->write_ephy_reg(pdata, REG_MII_ADVERTISE, adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
ret = hw_ops->read_ephy_reg(pdata, REG_MII_CTRL1000, &adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
adv &= ~REG_BIT_ADVERTISE_1000_CAP;
|
|
adv |= ethtool_adv_to_mii_ctrl1000_t(advertising);
|
|
ret = hw_ops->write_ephy_reg(pdata, REG_MII_CTRL1000, adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
|
|
ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, &adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
adv = FXGMAC_SET_REG_BITS(adv, PHY_CR_AUTOENG_POS, PHY_CR_AUTOENG_LEN, 1);
|
|
ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
|
|
ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, &adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
adv = FXGMAC_SET_REG_BITS(adv, PHY_CR_RE_AUTOENG_POS, PHY_CR_RE_AUTOENG_LEN, 1);
|
|
ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, adv);
|
|
if (ret < 0)
|
|
return -ETIMEDOUT;
|
|
} else {
|
|
pdata->phy_duplex = cmd->base.duplex;
|
|
pdata->phy_speed = cmd->base.speed;
|
|
fxgmac_phy_force_speed(pdata, pdata->phy_speed);
|
|
fxgmac_phy_force_duplex(pdata, pdata->phy_duplex);
|
|
fxgmac_phy_force_autoneg(pdata, pdata->phy_autoeng);
|
|
//hw_ops->config_mac_speed(pdata);
|
|
}
|
|
|
|
ret = fxgmac_ephy_soft_reset(pdata);
|
|
if (ret) {
|
|
printk("%s: ephy soft reset timeout.\n", __func__);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fxgmac_get_pauseparam(struct net_device *netdev,
|
|
struct ethtool_pauseparam *pause)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
|
|
pause->autoneg = 1;
|
|
pause->rx_pause = pdata->rx_pause;
|
|
pause->tx_pause = pdata->tx_pause;
|
|
|
|
DPRINTK("fxmac get_pauseparam done, rx=%d, tx=%d\n", pdata->rx_pause, pdata->tx_pause);
|
|
}
|
|
|
|
static int fxgmac_set_pauseparam(struct net_device *netdev,
|
|
struct ethtool_pauseparam *pause)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops;
|
|
unsigned int pre_rx_pause = pdata->rx_pause;
|
|
unsigned int pre_tx_pause = pdata->tx_pause;
|
|
|
|
//if (pause->autoneg == AUTONEG_ENABLE)
|
|
//DPRINTK("fxgmac set pause parameter, autoneg=%d\n", pause->autoneg);
|
|
|
|
pdata->rx_pause = pause->rx_pause;
|
|
pdata->tx_pause = pause->tx_pause;
|
|
|
|
if(pre_rx_pause != pdata->rx_pause) {
|
|
hw_ops->config_rx_flow_control(pdata);
|
|
DPRINTK("fxgmac set pause parameter, rx from %d to %d\n", pre_rx_pause, pdata->rx_pause);
|
|
}
|
|
if(pre_tx_pause != pdata->tx_pause) {
|
|
hw_ops->config_tx_flow_control(pdata);
|
|
DPRINTK("fxgmac set pause parameter, tx from %d to %d\n", pre_tx_pause, pdata->tx_pause);
|
|
}
|
|
|
|
/*
|
|
if (netif_running(netdev))
|
|
fxgmac_restart_dev(pdata);
|
|
*/
|
|
DPRINTK("fxgmac set pause parameter, autoneg=%d, rx=%d, tx=%d\n", pause->autoneg, pause->rx_pause, pause->tx_pause);
|
|
|
|
return 0;
|
|
}
|
|
#endif /*FXGMAC_PAUSE_FEATURE_ENABLED*/
|
|
|
|
|
|
/* yzhang added for debug sake. descriptors status checking
|
|
* 2021.03.29
|
|
*/
|
|
#define FXGMAC_ETH_GSTRING_LEN 32
|
|
|
|
#if 0
|
|
static char fxgmac_gstrings_test[1024*7][FXGMAC_ETH_GSTRING_LEN]= {
|
|
};
|
|
|
|
static char fxgmac_gstrings_header[7][10]= {
|
|
"Channel:", "curDesc:",
|
|
"baseMem:", "desc0 :",
|
|
"desc1 :", "desc2 :",
|
|
"desc3 :"
|
|
};
|
|
#endif
|
|
|
|
#define FXGMAC_TEST_LEN (sizeof(fxgmac_gstrings_test) / FXGMAC_ETH_GSTRING_LEN)
|
|
#define DBG_ETHTOOL_CHECK_NUM_OF_DESC 5
|
|
//static u16 fxgmac_ethtool_test_len = DBG_ETHTOOL_CHECK_NUM_OF_DESC; //default value, yzhang
|
|
|
|
static void fxgmac_ethtool_get_strings(struct net_device *netdev,
|
|
u32 stringset, u8 *data)
|
|
{
|
|
int i;
|
|
|
|
switch (stringset) {
|
|
case ETH_SS_STATS:
|
|
for (i = 0; i < FXGMAC_STATS_COUNT; i++) {
|
|
memcpy(data, fxgmac_gstring_stats[i].stat_string,
|
|
strlen(fxgmac_gstring_stats[i].stat_string));
|
|
data += ETH_GSTRING_LEN;
|
|
}
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int fxgmac_ethtool_get_sset_count(struct net_device *netdev,
|
|
int stringset)
|
|
{
|
|
int ret;
|
|
|
|
switch (stringset) {
|
|
case ETH_SS_STATS:
|
|
ret = FXGMAC_STATS_COUNT;
|
|
break;
|
|
|
|
default:
|
|
ret = -EOPNOTSUPP;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void fxgmac_ethtool_get_ethtool_stats(struct net_device *netdev,
|
|
struct ethtool_stats *stats,
|
|
u64 *data)
|
|
{
|
|
struct fxgmac_pdata *pdata = netdev_priv(netdev);
|
|
u8 *stat;
|
|
int i;
|
|
|
|
#if FXGMAC_PM_FEATURE_ENABLED
|
|
/* 20210709 for net power down */
|
|
if(!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate))
|
|
#endif
|
|
{
|
|
//DPRINTK("fxgmac_ethtool_get_ethtool_stats, here\n");
|
|
pdata->hw_ops.read_mmc_stats(pdata);
|
|
}
|
|
|
|
for (i = 0; i < FXGMAC_STATS_COUNT; i++) {
|
|
stat = (u8 *)pdata + fxgmac_gstring_stats[i].stat_offset;
|
|
*data++ = *(u64 *)stat;
|
|
}
|
|
}
|
|
|
|
static inline bool fxgmac_removed(void __iomem *addr)
|
|
{
|
|
return unlikely(!addr);
|
|
}
|
|
#define FXGMAC_REMOVED(a) fxgmac_removed(a)
|
|
|
|
static const struct ethtool_ops fxgmac_ethtool_ops = {
|
|
.get_drvinfo = fxgmac_ethtool_get_drvinfo,
|
|
.get_link = ethtool_op_get_link,
|
|
.get_msglevel = fxgmac_ethtool_get_msglevel,
|
|
.set_msglevel = fxgmac_ethtool_set_msglevel,
|
|
.get_channels = fxgmac_ethtool_get_channels,
|
|
.get_coalesce = fxgmac_ethtool_get_coalesce,
|
|
.set_coalesce = fxgmac_ethtool_set_coalesce,
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,10,0))
|
|
/*
|
|
* The process of set is to get first and then set,
|
|
* and the result of get is preserved for values that have not been modified.
|
|
*
|
|
* Therefore, when using, it is necessary to ensure that this macro and the
|
|
* assignment operation in the get_coalesce are one-to-one correspondence,
|
|
* otherwise the macro and parameters will be verified when set, and the error
|
|
* of "Operation not supported " will be reported if the verification fails
|
|
*/
|
|
#ifdef ETHTOOL_COALESCE_USECS
|
|
.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
|
|
#endif
|
|
#endif
|
|
.get_strings = fxgmac_ethtool_get_strings,
|
|
.get_sset_count = fxgmac_ethtool_get_sset_count,
|
|
.get_ethtool_stats = fxgmac_ethtool_get_ethtool_stats,
|
|
.get_regs_len = fxgmac_get_regs_len,
|
|
.get_regs = fxgmac_get_regs,
|
|
.get_ringparam = fxgmac_get_ringparam,
|
|
.set_ringparam = fxgmac_set_ringparam,
|
|
#if (FXGMAC_RSS_FEATURE_ENABLED)
|
|
.get_rxnfc = fxgmac_get_rxnfc,
|
|
.set_rxnfc = fxgmac_set_rxnfc,
|
|
.get_rxfh_indir_size = fxgmac_rss_indir_size,
|
|
.get_rxfh_key_size = fxgmac_get_rxfh_key_size,
|
|
.get_rxfh = fxgmac_get_rxfh,
|
|
.set_rxfh = fxgmac_set_rxfh,
|
|
#endif
|
|
#if (FXGMAC_WOL_FEATURE_ENABLED)
|
|
.get_wol = fxgmac_get_wol,
|
|
.set_wol = fxgmac_set_wol,
|
|
#endif
|
|
#if (FXGMAC_PAUSE_FEATURE_ENABLED)
|
|
#ifdef ETHTOOL_GLINKSETTINGS
|
|
.get_link_ksettings = fxgmac_get_link_ksettings,
|
|
.set_link_ksettings = fxgmac_set_link_ksettings,
|
|
#endif /* ETHTOOL_GLINKSETTINGS */
|
|
.get_pauseparam = fxgmac_get_pauseparam,
|
|
.set_pauseparam = fxgmac_set_pauseparam,
|
|
#endif
|
|
};
|
|
|
|
const struct ethtool_ops *fxgmac_get_ethtool_ops(void)
|
|
{
|
|
return &fxgmac_ethtool_ops;
|
|
}
|