Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd.
#
obj-$(CONFIG_LIBWX) += libwx.o
libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o
libwx-objs += wx_vf.o wx_vf_lib.o wx_vf_common.o
@@ -0,0 +1,795 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
#include <linux/pci.h>
#include <linux/phy.h>
#include <linux/ethtool.h>
#include "wx_type.h"
#include "wx_ethtool.h"
#include "wx_hw.h"
#include "wx_lib.h"
struct wx_stats {
char stat_string[ETH_GSTRING_LEN];
size_t sizeof_stat;
off_t stat_offset;
};
#define WX_STAT(str, m) { \
.stat_string = str, \
.sizeof_stat = sizeof(((struct wx *)0)->m), \
.stat_offset = offsetof(struct wx, m) }
static const struct wx_stats wx_gstrings_stats[] = {
WX_STAT("rx_dma_pkts", stats.gprc),
WX_STAT("tx_dma_pkts", stats.gptc),
WX_STAT("rx_dma_bytes", stats.gorc),
WX_STAT("tx_dma_bytes", stats.gotc),
WX_STAT("rx_total_pkts", stats.tpr),
WX_STAT("tx_total_pkts", stats.tpt),
WX_STAT("rx_long_length_count", stats.roc),
WX_STAT("rx_short_length_count", stats.ruc),
WX_STAT("os2bmc_rx_by_bmc", stats.o2bgptc),
WX_STAT("os2bmc_tx_by_bmc", stats.b2ospc),
WX_STAT("os2bmc_tx_by_host", stats.o2bspc),
WX_STAT("os2bmc_rx_by_host", stats.b2ogprc),
WX_STAT("rx_no_dma_resources", stats.rdmdrop),
WX_STAT("tx_busy", tx_busy),
WX_STAT("non_eop_descs", non_eop_descs),
WX_STAT("tx_restart_queue", restart_queue),
WX_STAT("rx_csum_offload_good_count", hw_csum_rx_good),
WX_STAT("rx_csum_offload_errors", hw_csum_rx_error),
WX_STAT("alloc_rx_buff_failed", alloc_rx_buff_failed),
WX_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
WX_STAT("tx_hwtstamp_skipped", tx_hwtstamp_skipped),
WX_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
};
static const struct wx_stats wx_gstrings_fdir_stats[] = {
WX_STAT("fdir_match", stats.fdirmatch),
WX_STAT("fdir_miss", stats.fdirmiss),
};
static const struct wx_stats wx_gstrings_rsc_stats[] = {
WX_STAT("rsc_aggregated", rsc_count),
WX_STAT("rsc_flushed", rsc_flush),
};
/* drivers allocates num_tx_queues and num_rx_queues symmetrically so
* we set the num_rx_queues to evaluate to num_tx_queues. This is
* used because we do not have a good way to get the max number of
* rx queues with CONFIG_RPS disabled.
*/
#define WX_NUM_RX_QUEUES netdev->num_tx_queues
#define WX_NUM_TX_QUEUES netdev->num_tx_queues
#define WX_QUEUE_STATS_LEN ( \
(WX_NUM_TX_QUEUES + WX_NUM_RX_QUEUES) * \
(sizeof(struct wx_queue_stats) / sizeof(u64)))
#define WX_GLOBAL_STATS_LEN ARRAY_SIZE(wx_gstrings_stats)
#define WX_FDIR_STATS_LEN ARRAY_SIZE(wx_gstrings_fdir_stats)
#define WX_RSC_STATS_LEN ARRAY_SIZE(wx_gstrings_rsc_stats)
#define WX_STATS_LEN (WX_GLOBAL_STATS_LEN + WX_QUEUE_STATS_LEN)
int wx_get_sset_count(struct net_device *netdev, int sset)
{
struct wx *wx = netdev_priv(netdev);
int len = WX_STATS_LEN;
switch (sset) {
case ETH_SS_STATS:
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
len += WX_FDIR_STATS_LEN;
if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags))
len += WX_RSC_STATS_LEN;
return len;
default:
return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(wx_get_sset_count);
void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
{
struct wx *wx = netdev_priv(netdev);
u8 *p = data;
int i;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < WX_GLOBAL_STATS_LEN; i++)
ethtool_puts(&p, wx_gstrings_stats[i].stat_string);
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)) {
for (i = 0; i < WX_FDIR_STATS_LEN; i++)
ethtool_puts(&p, wx_gstrings_fdir_stats[i].stat_string);
}
if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags)) {
for (i = 0; i < WX_RSC_STATS_LEN; i++)
ethtool_puts(&p, wx_gstrings_rsc_stats[i].stat_string);
}
for (i = 0; i < netdev->num_tx_queues; i++) {
ethtool_sprintf(&p, "tx_queue_%u_packets", i);
ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
}
for (i = 0; i < WX_NUM_RX_QUEUES; i++) {
ethtool_sprintf(&p, "rx_queue_%u_packets", i);
ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
}
break;
}
}
EXPORT_SYMBOL(wx_get_strings);
void wx_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data)
{
struct wx *wx = netdev_priv(netdev);
struct wx_ring *ring;
unsigned int start;
int i, j, k;
char *p;
wx_update_stats(wx);
for (i = 0; i < WX_GLOBAL_STATS_LEN; i++) {
p = (char *)wx + wx_gstrings_stats[i].stat_offset;
data[i] = (wx_gstrings_stats[i].sizeof_stat ==
sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
}
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)) {
for (k = 0; k < WX_FDIR_STATS_LEN; k++) {
p = (char *)wx + wx_gstrings_fdir_stats[k].stat_offset;
data[i++] = *(u64 *)p;
}
}
if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags)) {
for (k = 0; k < WX_RSC_STATS_LEN; k++) {
p = (char *)wx + wx_gstrings_rsc_stats[k].stat_offset;
data[i++] = *(u64 *)p;
}
}
for (j = 0; j < netdev->num_tx_queues; j++) {
ring = wx->tx_ring[j];
if (!ring) {
data[i++] = 0;
data[i++] = 0;
continue;
}
do {
start = u64_stats_fetch_begin(&ring->syncp);
data[i] = ring->stats.packets;
data[i + 1] = ring->stats.bytes;
} while (u64_stats_fetch_retry(&ring->syncp, start));
i += 2;
}
for (j = 0; j < WX_NUM_RX_QUEUES; j++) {
ring = wx->rx_ring[j];
if (!ring) {
data[i++] = 0;
data[i++] = 0;
continue;
}
do {
start = u64_stats_fetch_begin(&ring->syncp);
data[i] = ring->stats.packets;
data[i + 1] = ring->stats.bytes;
} while (u64_stats_fetch_retry(&ring->syncp, start));
i += 2;
}
}
EXPORT_SYMBOL(wx_get_ethtool_stats);
void wx_get_mac_stats(struct net_device *netdev,
struct ethtool_eth_mac_stats *mac_stats)
{
struct wx *wx = netdev_priv(netdev);
struct wx_hw_stats *hwstats;
wx_update_stats(wx);
hwstats = &wx->stats;
mac_stats->MulticastFramesXmittedOK = hwstats->mptc;
mac_stats->BroadcastFramesXmittedOK = hwstats->bptc;
mac_stats->MulticastFramesReceivedOK = hwstats->mprc;
mac_stats->BroadcastFramesReceivedOK = hwstats->bprc;
}
EXPORT_SYMBOL(wx_get_mac_stats);
void wx_get_pause_stats(struct net_device *netdev,
struct ethtool_pause_stats *stats)
{
struct wx *wx = netdev_priv(netdev);
struct wx_hw_stats *hwstats;
wx_update_stats(wx);
hwstats = &wx->stats;
stats->tx_pause_frames = hwstats->lxontxc + hwstats->lxofftxc;
stats->rx_pause_frames = hwstats->lxonrxc + hwstats->lxoffrxc;
}
EXPORT_SYMBOL(wx_get_pause_stats);
void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
{
unsigned int stats_len = WX_STATS_LEN;
struct wx *wx = netdev_priv(netdev);
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
stats_len += WX_FDIR_STATS_LEN;
strscpy(info->driver, wx->driver_name, sizeof(info->driver));
strscpy(info->fw_version, wx->eeprom_id, sizeof(info->fw_version));
strscpy(info->bus_info, pci_name(wx->pdev), sizeof(info->bus_info));
if (wx->num_tx_queues <= WX_NUM_TX_QUEUES) {
info->n_stats = stats_len -
(WX_NUM_TX_QUEUES - wx->num_tx_queues) *
(sizeof(struct wx_queue_stats) / sizeof(u64)) * 2;
} else {
info->n_stats = stats_len;
}
}
EXPORT_SYMBOL(wx_get_drvinfo);
int wx_nway_reset(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
return phylink_ethtool_nway_reset(wx->phylink);
}
EXPORT_SYMBOL(wx_nway_reset);
int wx_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
struct wx *wx = netdev_priv(netdev);
return phylink_ethtool_ksettings_get(wx->phylink, cmd);
}
EXPORT_SYMBOL(wx_get_link_ksettings);
int wx_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd)
{
struct wx *wx = netdev_priv(netdev);
return phylink_ethtool_ksettings_set(wx->phylink, cmd);
}
EXPORT_SYMBOL(wx_set_link_ksettings);
void wx_get_wol(struct net_device *netdev,
struct ethtool_wolinfo *wol)
{
struct wx *wx = netdev_priv(netdev);
if (!wx->wol_hw_supported)
return;
wol->supported = WAKE_MAGIC;
wol->wolopts = 0;
if (wx->wol & WX_PSR_WKUP_CTL_MAG)
wol->wolopts |= WAKE_MAGIC;
}
EXPORT_SYMBOL(wx_get_wol);
int wx_set_wol(struct net_device *netdev,
struct ethtool_wolinfo *wol)
{
struct wx *wx = netdev_priv(netdev);
struct pci_dev *pdev = wx->pdev;
if (!wx->wol_hw_supported)
return -EOPNOTSUPP;
wx->wol = 0;
if (wol->wolopts & WAKE_MAGIC)
wx->wol = WX_PSR_WKUP_CTL_MAG;
wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
device_set_wakeup_enable(&pdev->dev, !!(wx->wol));
return 0;
}
EXPORT_SYMBOL(wx_set_wol);
void wx_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct wx *wx = netdev_priv(netdev);
phylink_ethtool_get_pauseparam(wx->phylink, pause);
}
EXPORT_SYMBOL(wx_get_pauseparam);
int wx_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
struct wx *wx = netdev_priv(netdev);
return phylink_ethtool_set_pauseparam(wx->phylink, pause);
}
EXPORT_SYMBOL(wx_set_pauseparam);
void wx_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kernel_ring,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(netdev);
ring->rx_max_pending = WX_MAX_RXD;
ring->tx_max_pending = WX_MAX_TXD;
ring->rx_mini_max_pending = 0;
ring->rx_jumbo_max_pending = 0;
ring->rx_pending = wx->rx_ring_count;
ring->tx_pending = wx->tx_ring_count;
ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0;
}
EXPORT_SYMBOL(wx_get_ringparam);
int wx_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(netdev);
ec->tx_max_coalesced_frames_irq = wx->tx_work_limit;
/* only valid if in constant ITR mode */
if (wx->rx_itr_setting <= 1)
ec->rx_coalesce_usecs = wx->rx_itr_setting;
else
ec->rx_coalesce_usecs = wx->rx_itr_setting >> 2;
if (wx->adaptive_itr) {
ec->use_adaptive_rx_coalesce = 1;
ec->use_adaptive_tx_coalesce = 1;
}
/* if in mixed tx/rx queues per vector mode, report only rx settings */
if (wx->q_vector[0]->tx.count && wx->q_vector[0]->rx.count)
return 0;
/* only valid if in constant ITR mode */
if (wx->tx_itr_setting <= 1)
ec->tx_coalesce_usecs = wx->tx_itr_setting;
else
ec->tx_coalesce_usecs = wx->tx_itr_setting >> 2;
return 0;
}
EXPORT_SYMBOL(wx_get_coalesce);
static void wx_update_rsc(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
bool need_reset = false;
/* nothing to do if LRO or RSC are not enabled */
if (!test_bit(WX_FLAG_RSC_CAPABLE, wx->flags) ||
!(netdev->features & NETIF_F_LRO))
return;
/* check the feature flag value and enable RSC if necessary */
if (wx->rx_itr_setting == 1 ||
wx->rx_itr_setting > WX_MIN_RSC_ITR) {
if (!test_bit(WX_FLAG_RSC_ENABLED, wx->flags)) {
set_bit(WX_FLAG_RSC_ENABLED, wx->flags);
dev_info(&wx->pdev->dev,
"rx-usecs value high enough to re-enable RSC\n");
need_reset = true;
}
/* if interrupt rate is too high then disable RSC */
} else if (test_bit(WX_FLAG_RSC_ENABLED, wx->flags)) {
clear_bit(WX_FLAG_RSC_ENABLED, wx->flags);
dev_info(&wx->pdev->dev,
"rx-usecs set too low, disabling RSC\n");
need_reset = true;
}
/* reset the device to apply the new RSC setting */
if (need_reset && wx->do_reset)
wx->do_reset(netdev);
}
int wx_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(netdev);
u16 tx_itr_param, rx_itr_param;
struct wx_q_vector *q_vector;
u16 max_eitr;
int i;
if (wx->q_vector[0]->tx.count && wx->q_vector[0]->rx.count) {
/* reject Tx specific changes in case of mixed RxTx vectors */
if (ec->tx_coalesce_usecs)
return -EOPNOTSUPP;
}
if (ec->tx_max_coalesced_frames_irq > U16_MAX ||
!ec->tx_max_coalesced_frames_irq)
return -EINVAL;
wx->tx_work_limit = ec->tx_max_coalesced_frames_irq;
switch (wx->mac.type) {
case wx_mac_sp:
max_eitr = WX_SP_MAX_EITR;
rx_itr_param = WX_20K_ITR;
tx_itr_param = WX_12K_ITR;
break;
case wx_mac_aml:
case wx_mac_aml40:
max_eitr = WX_AML_MAX_EITR;
rx_itr_param = WX_20K_ITR;
tx_itr_param = WX_12K_ITR;
break;
default:
max_eitr = WX_EM_MAX_EITR;
rx_itr_param = WX_7K_ITR;
tx_itr_param = WX_7K_ITR;
break;
}
if ((ec->rx_coalesce_usecs > (max_eitr >> 2)) ||
(ec->tx_coalesce_usecs > (max_eitr >> 2)))
return -EINVAL;
if (ec->use_adaptive_rx_coalesce) {
wx->adaptive_itr = true;
wx->rx_itr_setting = 1;
wx->tx_itr_setting = 1;
return 0;
}
if (ec->rx_coalesce_usecs > 1)
wx->rx_itr_setting = ec->rx_coalesce_usecs << 2;
else
wx->rx_itr_setting = ec->rx_coalesce_usecs;
if (ec->tx_coalesce_usecs > 1)
wx->tx_itr_setting = ec->tx_coalesce_usecs << 2;
else
wx->tx_itr_setting = ec->tx_coalesce_usecs;
if (wx->adaptive_itr) {
wx->adaptive_itr = false;
wx->rx_itr_setting = rx_itr_param;
wx->tx_itr_setting = tx_itr_param;
} else if (wx->rx_itr_setting == 1 || wx->tx_itr_setting == 1) {
wx->adaptive_itr = true;
}
if (wx->rx_itr_setting != 1)
rx_itr_param = wx->rx_itr_setting;
if (wx->tx_itr_setting != 1)
tx_itr_param = wx->tx_itr_setting;
/* mixed Rx/Tx */
if (wx->q_vector[0]->tx.count && wx->q_vector[0]->rx.count)
wx->tx_itr_setting = wx->rx_itr_setting;
for (i = 0; i < wx->num_q_vectors; i++) {
q_vector = wx->q_vector[i];
if (q_vector->tx.count && !q_vector->rx.count)
/* tx only */
q_vector->itr = tx_itr_param;
else
/* rx only or mixed */
q_vector->itr = rx_itr_param;
wx_write_eitr(q_vector);
}
wx_update_rsc(wx);
return 0;
}
EXPORT_SYMBOL(wx_set_coalesce);
static unsigned int wx_max_channels(struct wx *wx)
{
unsigned int max_combined;
if (!wx->msix_q_entries) {
/* We only support one q_vector without MSI-X */
max_combined = 1;
} else {
/* support up to max allowed queues with RSS */
if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags))
max_combined = 63;
else
max_combined = 8;
}
return max_combined;
}
void wx_get_channels(struct net_device *dev,
struct ethtool_channels *ch)
{
struct wx *wx = netdev_priv(dev);
/* report maximum channels */
ch->max_combined = wx_max_channels(wx);
/* report info for other vector */
if (wx->msix_q_entries) {
ch->max_other = 1;
ch->other_count = 1;
}
/* record RSS queues */
ch->combined_count = wx->ring_feature[RING_F_RSS].indices;
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
ch->combined_count = wx->ring_feature[RING_F_FDIR].indices;
}
EXPORT_SYMBOL(wx_get_channels);
int wx_set_channels(struct net_device *dev,
struct ethtool_channels *ch)
{
unsigned int count = ch->combined_count;
struct wx *wx = netdev_priv(dev);
/* verify other_count has not changed */
if (ch->other_count != 1)
return -EINVAL;
/* verify the number of channels does not exceed hardware limits */
if (count > wx_max_channels(wx))
return -EINVAL;
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
wx->ring_feature[RING_F_FDIR].limit = count;
wx->ring_feature[RING_F_RSS].limit = count;
return wx->setup_tc(dev, netdev_get_num_tc(dev));
}
EXPORT_SYMBOL(wx_set_channels);
u32 wx_rss_indir_size(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
return wx_rss_indir_tbl_entries(wx);
}
EXPORT_SYMBOL(wx_rss_indir_size);
u32 wx_get_rxfh_key_size(struct net_device *netdev)
{
return WX_RSS_KEY_SIZE;
}
EXPORT_SYMBOL(wx_get_rxfh_key_size);
static void wx_get_reta(struct wx *wx, u32 *indir)
{
u32 reta_size = wx_rss_indir_tbl_entries(wx);
u16 rss_m = wx->ring_feature[RING_F_RSS].mask;
if (test_bit(WX_FLAG_SRIOV_ENABLED, wx->flags))
rss_m = wx->ring_feature[RING_F_RSS].indices - 1;
for (u32 i = 0; i < reta_size; i++)
indir[i] = wx->rss_indir_tbl[i] & rss_m;
}
int wx_get_rxfh(struct net_device *netdev,
struct ethtool_rxfh_param *rxfh)
{
struct wx *wx = netdev_priv(netdev);
rxfh->hfunc = ETH_RSS_HASH_TOP;
if (rxfh->indir)
wx_get_reta(wx, rxfh->indir);
if (rxfh->key)
memcpy(rxfh->key, wx->rss_key, WX_RSS_KEY_SIZE);
return 0;
}
EXPORT_SYMBOL(wx_get_rxfh);
int wx_set_rxfh(struct net_device *netdev,
struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(netdev);
u32 reta_entries, i;
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
rxfh->hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
reta_entries = wx_rss_indir_tbl_entries(wx);
/* Fill out the redirection table */
if (rxfh->indir) {
for (i = 0; i < reta_entries; i++)
wx->rss_indir_tbl[i] = rxfh->indir[i];
wx_store_reta(wx);
}
/* Fill out the rss hash key */
if (rxfh->key) {
memcpy(wx->rss_key, rxfh->key, WX_RSS_KEY_SIZE);
wx_store_rsskey(wx);
}
return 0;
}
EXPORT_SYMBOL(wx_set_rxfh);
static const struct wx_rss_flow_map rss_flow_table[] = {
{ TCP_V4_FLOW, RXH_L4_B_0_1 | RXH_L4_B_2_3, WX_RSS_FIELD_IPV4_TCP },
{ TCP_V6_FLOW, RXH_L4_B_0_1 | RXH_L4_B_2_3, WX_RSS_FIELD_IPV6_TCP },
{ UDP_V4_FLOW, RXH_L4_B_0_1 | RXH_L4_B_2_3, WX_RSS_FIELD_IPV4_UDP },
{ UDP_V6_FLOW, RXH_L4_B_0_1 | RXH_L4_B_2_3, WX_RSS_FIELD_IPV6_UDP },
{ SCTP_V4_FLOW, RXH_L4_B_0_1 | RXH_L4_B_2_3, WX_RSS_FIELD_IPV4_SCTP },
{ SCTP_V6_FLOW, RXH_L4_B_0_1 | RXH_L4_B_2_3, WX_RSS_FIELD_IPV6_SCTP },
};
int wx_get_rxfh_fields(struct net_device *dev,
struct ethtool_rxfh_fields *nfc)
{
struct wx *wx = netdev_priv(dev);
nfc->data = RXH_IP_SRC | RXH_IP_DST;
for (u32 i = 0; i < ARRAY_SIZE(rss_flow_table); i++) {
const struct wx_rss_flow_map *entry = &rss_flow_table[i];
if (entry->flow_type == nfc->flow_type) {
if (wx->rss_flags & entry->flag)
nfc->data |= entry->data;
break;
}
}
return 0;
}
EXPORT_SYMBOL(wx_get_rxfh_fields);
int wx_set_rxfh_fields(struct net_device *dev,
const struct ethtool_rxfh_fields *nfc,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(dev);
u8 flags = wx->rss_flags;
if (!(nfc->data & RXH_IP_SRC) ||
!(nfc->data & RXH_IP_DST))
return -EINVAL;
for (u32 i = 0; i < ARRAY_SIZE(rss_flow_table); i++) {
const struct wx_rss_flow_map *entry = &rss_flow_table[i];
if (entry->flow_type == nfc->flow_type) {
if (nfc->data & entry->data)
flags |= entry->flag;
else
flags &= ~entry->flag;
if (flags != wx->rss_flags) {
wx->rss_flags = flags;
wx_config_rss_field(wx);
}
return 0;
}
}
return -EINVAL;
}
EXPORT_SYMBOL(wx_set_rxfh_fields);
u32 wx_get_msglevel(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
return wx->msg_enable;
}
EXPORT_SYMBOL(wx_get_msglevel);
void wx_set_msglevel(struct net_device *netdev, u32 data)
{
struct wx *wx = netdev_priv(netdev);
wx->msg_enable = data;
}
EXPORT_SYMBOL(wx_set_msglevel);
int wx_get_ts_info(struct net_device *dev,
struct kernel_ethtool_ts_info *info)
{
struct wx *wx = netdev_priv(dev);
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
BIT(HWTSTAMP_FILTER_PTP_V2_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
BIT(HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if (wx->ptp_clock)
info->phc_index = ptp_clock_index(wx->ptp_clock);
else
info->phc_index = -1;
info->tx_types = BIT(HWTSTAMP_TX_OFF) |
BIT(HWTSTAMP_TX_ON);
return 0;
}
EXPORT_SYMBOL(wx_get_ts_info);
void wx_get_ptp_stats(struct net_device *dev,
struct ethtool_ts_stats *ts_stats)
{
struct wx *wx = netdev_priv(dev);
if (wx->ptp_clock) {
ts_stats->pkts = wx->tx_hwtstamp_pkts;
ts_stats->lost = wx->tx_hwtstamp_timeouts +
wx->tx_hwtstamp_skipped +
wx->rx_hwtstamp_cleared;
ts_stats->err = wx->tx_hwtstamp_errors;
}
}
EXPORT_SYMBOL(wx_get_ptp_stats);
static int wx_get_link_ksettings_vf(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
struct wx *wx = netdev_priv(netdev);
ethtool_link_ksettings_zero_link_mode(cmd, supported);
cmd->base.autoneg = AUTONEG_DISABLE;
cmd->base.port = PORT_NONE;
cmd->base.duplex = DUPLEX_FULL;
cmd->base.speed = wx->speed;
return 0;
}
static const struct ethtool_ops wx_ethtool_ops_vf = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ |
ETHTOOL_COALESCE_USE_ADAPTIVE,
.get_drvinfo = wx_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ringparam = wx_get_ringparam,
.get_msglevel = wx_get_msglevel,
.get_coalesce = wx_get_coalesce,
.get_ts_info = ethtool_op_get_ts_info,
.get_link_ksettings = wx_get_link_ksettings_vf,
};
void wx_set_ethtool_ops_vf(struct net_device *netdev)
{
netdev->ethtool_ops = &wx_ethtool_ops_vf;
}
EXPORT_SYMBOL(wx_set_ethtool_ops_vf);
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_ETHTOOL_H_
#define _WX_ETHTOOL_H_
int wx_get_sset_count(struct net_device *netdev, int sset);
void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data);
void wx_get_ethtool_stats(struct net_device *netdev,
struct ethtool_stats *stats, u64 *data);
void wx_get_mac_stats(struct net_device *netdev,
struct ethtool_eth_mac_stats *mac_stats);
void wx_get_pause_stats(struct net_device *netdev,
struct ethtool_pause_stats *stats);
void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info);
int wx_nway_reset(struct net_device *netdev);
int wx_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd);
int wx_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd);
void wx_get_wol(struct net_device *netdev,
struct ethtool_wolinfo *wol);
int wx_set_wol(struct net_device *netdev,
struct ethtool_wolinfo *wol);
void wx_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause);
int wx_set_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause);
void wx_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kernel_ring,
struct netlink_ext_ack *extack);
int wx_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack);
int wx_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
struct kernel_ethtool_coalesce *kernel_coal,
struct netlink_ext_ack *extack);
void wx_get_channels(struct net_device *dev,
struct ethtool_channels *ch);
int wx_set_channels(struct net_device *dev,
struct ethtool_channels *ch);
u32 wx_rss_indir_size(struct net_device *netdev);
u32 wx_get_rxfh_key_size(struct net_device *netdev);
int wx_get_rxfh(struct net_device *netdev,
struct ethtool_rxfh_param *rxfh);
int wx_set_rxfh(struct net_device *netdev,
struct ethtool_rxfh_param *rxfh,
struct netlink_ext_ack *extack);
int wx_get_rxfh_fields(struct net_device *dev,
struct ethtool_rxfh_fields *cmd);
int wx_set_rxfh_fields(struct net_device *dev,
const struct ethtool_rxfh_fields *nfc,
struct netlink_ext_ack *extack);
u32 wx_get_msglevel(struct net_device *netdev);
void wx_set_msglevel(struct net_device *netdev, u32 data);
int wx_get_ts_info(struct net_device *dev,
struct kernel_ethtool_ts_info *info);
void wx_get_ptp_stats(struct net_device *dev,
struct ethtool_ts_stats *ts_stats);
void wx_set_ethtool_ops_vf(struct net_device *netdev);
#endif /* _WX_ETHTOOL_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2022 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_HW_H_
#define _WX_HW_H_
#include <linux/phy.h>
int wx_phy_read_reg_mdi_c22(struct mii_bus *bus, int phy_addr, int regnum);
int wx_phy_write_reg_mdi_c22(struct mii_bus *bus, int phy_addr, int regnum, u16 value);
int wx_phy_read_reg_mdi_c45(struct mii_bus *bus, int phy_addr, int devnum, int regnum);
int wx_phy_write_reg_mdi_c45(struct mii_bus *bus, int phy_addr,
int devnum, int regnum, u16 value);
void wx_intr_enable(struct wx *wx, u64 qmask);
void wx_irq_disable(struct wx *wx);
int wx_check_flash_load(struct wx *wx, u32 check_bit);
void wx_control_hw(struct wx *wx, bool drv);
int wx_mng_present(struct wx *wx);
int wx_host_interface_command(struct wx *wx, u32 *buffer,
u32 length, u32 timeout, bool return_data);
int wx_set_pps(struct wx *wx, bool enable, u64 nsec, u64 cycles);
int wx_read_ee_hostif(struct wx *wx, u16 offset, u16 *data);
int wx_read_ee_hostif_buffer(struct wx *wx,
u16 offset, u16 words, u16 *data);
void wx_init_eeprom_params(struct wx *wx);
void wx_get_mac_addr(struct wx *wx, u8 *mac_addr);
void wx_init_rx_addrs(struct wx *wx);
void wx_mac_set_default_filter(struct wx *wx, u8 *addr);
int wx_add_mac_filter(struct wx *wx, u8 *addr, u16 pool);
int wx_del_mac_filter(struct wx *wx, u8 *addr, u16 pool);
void wx_flush_sw_mac_table(struct wx *wx);
u32 wx_mta_vector(struct wx *wx, u8 *mc_addr);
int wx_set_mac(struct net_device *netdev, void *p);
void wx_disable_rx(struct wx *wx);
int wx_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting);
int wx_disable_sec_rx_path(struct wx *wx);
void wx_enable_sec_rx_path(struct wx *wx);
void wx_set_rx_mode(struct net_device *netdev);
int wx_change_mtu(struct net_device *netdev, int new_mtu);
void wx_disable_rx_queue(struct wx *wx, struct wx_ring *ring);
void wx_enable_rx_queue(struct wx *wx, struct wx_ring *ring);
u32 wx_rss_indir_tbl_entries(struct wx *wx);
void wx_store_reta(struct wx *wx);
void wx_store_rsskey(struct wx *wx);
void wx_config_rss_field(struct wx *wx);
void wx_enable_rss(struct wx *wx, bool enable);
void wx_configure_rx(struct wx *wx);
void wx_configure(struct wx *wx);
void wx_start_hw(struct wx *wx);
int wx_disable_pcie_master(struct wx *wx);
int wx_stop_adapter(struct wx *wx);
void wx_reset_mac(struct wx *wx);
void wx_reset_misc(struct wx *wx);
int wx_get_pcie_msix_counts(struct wx *wx, u16 *msix_count, u16 max_msix_count);
int wx_sw_init(struct wx *wx);
int wx_set_vfta(struct wx *wx, u32 vlan, u32 vind, bool vlan_on);
int wx_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid);
int wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid);
int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause);
void wx_update_stats(struct wx *wx);
void wx_clear_hw_cntrs(struct wx *wx);
#endif /* _WX_HW_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* WangXun Gigabit PCI Express Linux driver
* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd.
*/
#ifndef _WX_LIB_H_
#define _WX_LIB_H_
struct wx_dec_ptype wx_decode_ptype(const u8 ptype);
void wx_alloc_rx_buffers(struct wx_ring *rx_ring, u16 cleaned_count);
u16 wx_desc_unused(struct wx_ring *ring);
netdev_tx_t wx_xmit_frame(struct sk_buff *skb,
struct net_device *netdev);
void wx_napi_enable_all(struct wx *wx);
void wx_napi_disable_all(struct wx *wx);
void wx_reset_interrupt_capability(struct wx *wx);
void wx_clear_interrupt_scheme(struct wx *wx);
int wx_init_interrupt_scheme(struct wx *wx);
irqreturn_t wx_msix_clean_rings(int __always_unused irq, void *data);
void wx_free_irq(struct wx *wx);
int wx_setup_isb_resources(struct wx *wx);
void wx_free_isb_resources(struct wx *wx);
u32 wx_misc_isb(struct wx *wx, enum wx_isb_idx idx);
void wx_write_eitr(struct wx_q_vector *q_vector);
void wx_configure_vectors(struct wx *wx);
void wx_clean_all_rx_rings(struct wx *wx);
void wx_clean_all_tx_rings(struct wx *wx);
void wx_free_resources(struct wx *wx);
int wx_setup_resources(struct wx *wx);
void wx_get_stats64(struct net_device *netdev,
struct rtnl_link_stats64 *stats);
int wx_set_features(struct net_device *netdev, netdev_features_t features);
netdev_features_t wx_fix_features(struct net_device *netdev,
netdev_features_t features);
netdev_features_t wx_features_check(struct sk_buff *skb,
struct net_device *netdev,
netdev_features_t features);
void wx_set_ring(struct wx *wx, u32 new_tx_count,
u32 new_rx_count, struct wx_ring *temp_ring);
void wx_service_event_schedule(struct wx *wx);
void wx_service_event_complete(struct wx *wx);
void wx_service_timer(struct timer_list *t);
#endif /* _WX_LIB_H_ */
@@ -0,0 +1,418 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_mbx.h"
/**
* wx_obtain_mbx_lock_pf - obtain mailbox lock
* @wx: pointer to the HW structure
* @vf: the VF index
*
* Return: return 0 on success and -EBUSY on failure
**/
static int wx_obtain_mbx_lock_pf(struct wx *wx, u16 vf)
{
int count = 5;
u32 mailbox;
while (count--) {
/* Take ownership of the buffer */
wr32(wx, WX_PXMAILBOX(vf), WX_PXMAILBOX_PFU);
/* reserve mailbox for vf use */
mailbox = rd32(wx, WX_PXMAILBOX(vf));
if (mailbox & WX_PXMAILBOX_PFU)
return 0;
else if (count)
udelay(10);
}
wx_err(wx, "Failed to obtain mailbox lock for PF%d", vf);
return -EBUSY;
}
static int wx_check_for_bit_pf(struct wx *wx, u32 mask, int index)
{
u32 mbvficr = rd32(wx, WX_MBVFICR(index));
if (!(mbvficr & mask))
return -EBUSY;
wr32(wx, WX_MBVFICR(index), mask);
return 0;
}
/**
* wx_check_for_ack_pf - checks to see if the VF has acked
* @wx: pointer to the HW structure
* @vf: the VF index
*
* Return: return 0 if the VF has set the status bit or else -EBUSY
**/
int wx_check_for_ack_pf(struct wx *wx, u16 vf)
{
u32 index = vf / 16, vf_bit = vf % 16;
return wx_check_for_bit_pf(wx,
FIELD_PREP(WX_MBVFICR_VFACK_MASK,
BIT(vf_bit)),
index);
}
/**
* wx_check_for_msg_pf - checks to see if the VF has sent mail
* @wx: pointer to the HW structure
* @vf: the VF index
*
* Return: return 0 if the VF has got req bit or else -EBUSY
**/
int wx_check_for_msg_pf(struct wx *wx, u16 vf)
{
u32 index = vf / 16, vf_bit = vf % 16;
return wx_check_for_bit_pf(wx,
FIELD_PREP(WX_MBVFICR_VFREQ_MASK,
BIT(vf_bit)),
index);
}
/**
* wx_write_mbx_pf - Places a message in the mailbox
* @wx: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @vf: the VF index
*
* Return: return 0 on success and -EINVAL/-EBUSY on failure
**/
int wx_write_mbx_pf(struct wx *wx, u32 *msg, u16 size, u16 vf)
{
struct wx_mbx_info *mbx = &wx->mbx;
int ret, i;
/* mbx->size is up to 15 */
if (size > mbx->size) {
wx_err(wx, "Invalid mailbox message size %d", size);
return -EINVAL;
}
/* lock the mailbox to prevent pf/vf race condition */
ret = wx_obtain_mbx_lock_pf(wx, vf);
if (ret)
return ret;
/* flush msg and acks as we are overwriting the message buffer */
wx_check_for_msg_pf(wx, vf);
wx_check_for_ack_pf(wx, vf);
/* copy the caller specified message to the mailbox memory buffer */
for (i = 0; i < size; i++)
wr32a(wx, WX_PXMBMEM(vf), i, msg[i]);
/* Interrupt VF to tell it a message has been sent and release buffer */
/* set mirrored mailbox flags */
wr32a(wx, WX_PXMBMEM(vf), WX_VXMAILBOX_SIZE, WX_PXMAILBOX_STS);
wr32(wx, WX_PXMAILBOX(vf), WX_PXMAILBOX_STS);
return 0;
}
/**
* wx_read_mbx_pf - Read a message from the mailbox
* @wx: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
* @vf: the VF index
*
* Return: return 0 on success and -EBUSY on failure
**/
int wx_read_mbx_pf(struct wx *wx, u32 *msg, u16 size, u16 vf)
{
struct wx_mbx_info *mbx = &wx->mbx;
int ret;
u16 i;
/* limit read to size of mailbox and mbx->size is up to 15 */
if (size > mbx->size)
size = mbx->size;
/* lock the mailbox to prevent pf/vf race condition */
ret = wx_obtain_mbx_lock_pf(wx, vf);
if (ret)
return ret;
for (i = 0; i < size; i++)
msg[i] = rd32a(wx, WX_PXMBMEM(vf), i);
/* Acknowledge the message and release buffer */
/* set mirrored mailbox flags */
wr32a(wx, WX_PXMBMEM(vf), WX_VXMAILBOX_SIZE, WX_PXMAILBOX_ACK);
wr32(wx, WX_PXMAILBOX(vf), WX_PXMAILBOX_ACK);
return 0;
}
/**
* wx_check_for_rst_pf - checks to see if the VF has reset
* @wx: pointer to the HW structure
* @vf: the VF index
*
* Return: return 0 on success and -EBUSY on failure
**/
int wx_check_for_rst_pf(struct wx *wx, u16 vf)
{
u32 reg_offset = WX_VF_REG_OFFSET(vf);
u32 vf_shift = WX_VF_IND_SHIFT(vf);
u32 vflre = 0;
vflre = rd32(wx, WX_VFLRE(reg_offset));
if (!(vflre & BIT(vf_shift)))
return -EBUSY;
wr32(wx, WX_VFLREC(reg_offset), BIT(vf_shift));
return 0;
}
static u32 wx_read_v2p_mailbox(struct wx *wx)
{
u32 mailbox = rd32(wx, WX_VXMAILBOX);
mailbox |= wx->mbx.mailbox;
wx->mbx.mailbox |= mailbox & WX_VXMAILBOX_R2C_BITS;
return mailbox;
}
static u32 wx_mailbox_get_lock_vf(struct wx *wx)
{
wr32(wx, WX_VXMAILBOX, WX_VXMAILBOX_VFU);
return wx_read_v2p_mailbox(wx);
}
/**
* wx_obtain_mbx_lock_vf - obtain mailbox lock
* @wx: pointer to the HW structure
*
* Return: return 0 on success and -EBUSY on failure
**/
static int wx_obtain_mbx_lock_vf(struct wx *wx)
{
int count = 5, ret;
u32 mailbox;
ret = readx_poll_timeout_atomic(wx_mailbox_get_lock_vf, wx, mailbox,
(mailbox & WX_VXMAILBOX_VFU),
1, count);
if (ret)
wx_err(wx, "Failed to obtain mailbox lock for VF.\n");
return ret;
}
static int wx_check_for_bit_vf(struct wx *wx, u32 mask)
{
u32 mailbox = wx_read_v2p_mailbox(wx);
wx->mbx.mailbox &= ~mask;
return (mailbox & mask ? 0 : -EBUSY);
}
/**
* wx_check_for_ack_vf - checks to see if the PF has ACK'd
* @wx: pointer to the HW structure
*
* Return: return 0 if the PF has set the status bit or else -EBUSY
**/
static int wx_check_for_ack_vf(struct wx *wx)
{
/* read clear the pf ack bit */
return wx_check_for_bit_vf(wx, WX_VXMAILBOX_PFACK);
}
/**
* wx_check_for_msg_vf - checks to see if the PF has sent mail
* @wx: pointer to the HW structure
*
* Return: return 0 if the PF has got req bit or else -EBUSY
**/
int wx_check_for_msg_vf(struct wx *wx)
{
/* read clear the pf sts bit */
return wx_check_for_bit_vf(wx, WX_VXMAILBOX_PFSTS);
}
/**
* wx_check_for_rst_vf - checks to see if the PF has reset
* @wx: pointer to the HW structure
*
* Return: return 0 if the PF has set the reset done and -EBUSY on failure
**/
int wx_check_for_rst_vf(struct wx *wx)
{
/* read clear the pf reset done bit */
return wx_check_for_bit_vf(wx,
WX_VXMAILBOX_RSTD |
WX_VXMAILBOX_RSTI);
}
/**
* wx_poll_for_msg - Wait for message notification
* @wx: pointer to the HW structure
*
* Return: return 0 if the VF has successfully received a message notification
**/
static int wx_poll_for_msg(struct wx *wx)
{
struct wx_mbx_info *mbx = &wx->mbx;
u32 val;
return readx_poll_timeout_atomic(wx_check_for_msg_vf, wx, val,
(val == 0), mbx->udelay, mbx->timeout);
}
/**
* wx_poll_for_ack - Wait for message acknowledgment
* @wx: pointer to the HW structure
*
* Return: return 0 if the VF has successfully received a message ack
**/
static int wx_poll_for_ack(struct wx *wx)
{
struct wx_mbx_info *mbx = &wx->mbx;
u32 val;
return readx_poll_timeout_atomic(wx_check_for_ack_vf, wx, val,
(val == 0), mbx->udelay, mbx->timeout);
}
/**
* wx_read_posted_mbx - Wait for message notification and receive message
* @wx: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
*
* Return: returns 0 if it successfully received a message notification and
* copied it into the receive buffer.
**/
int wx_read_posted_mbx(struct wx *wx, u32 *msg, u16 size)
{
int ret;
ret = wx_poll_for_msg(wx);
/* if ack received read message, otherwise we timed out */
if (ret)
return ret;
return wx_read_mbx_vf(wx, msg, size);
}
/**
* wx_write_posted_mbx - Write a message to the mailbox, wait for ack
* @wx: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
*
* Return: returns 0 if it successfully copied message into the buffer and
* received an ack to that message within delay * timeout period
**/
int wx_write_posted_mbx(struct wx *wx, u32 *msg, u16 size)
{
int ret;
/* send msg */
ret = wx_write_mbx_vf(wx, msg, size);
/* if msg sent wait until we receive an ack */
if (ret)
return ret;
return wx_poll_for_ack(wx);
}
/**
* wx_write_mbx_vf - Write a message to the mailbox
* @wx: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
*
* Return: returns 0 if it successfully copied message into the buffer
**/
int wx_write_mbx_vf(struct wx *wx, u32 *msg, u16 size)
{
struct wx_mbx_info *mbx = &wx->mbx;
int ret, i;
/* mbx->size is up to 15 */
if (size > mbx->size) {
wx_err(wx, "Invalid mailbox message size %d", size);
return -EINVAL;
}
/* lock the mailbox to prevent pf/vf race condition */
ret = wx_obtain_mbx_lock_vf(wx);
if (ret)
return ret;
/* flush msg and acks as we are overwriting the message buffer */
wx_check_for_msg_vf(wx);
wx_check_for_ack_vf(wx);
/* copy the caller specified message to the mailbox memory buffer */
for (i = 0; i < size; i++)
wr32a(wx, WX_VXMBMEM, i, msg[i]);
/* Drop VFU and interrupt the PF to tell it a message has been sent */
wr32(wx, WX_VXMAILBOX, WX_VXMAILBOX_REQ);
return 0;
}
/**
* wx_read_mbx_vf - Reads a message from the inbox intended for vf
* @wx: pointer to the HW structure
* @msg: The message buffer
* @size: Length of buffer
*
* Return: returns 0 if it successfully copied message into the buffer
**/
int wx_read_mbx_vf(struct wx *wx, u32 *msg, u16 size)
{
struct wx_mbx_info *mbx = &wx->mbx;
int ret, i;
/* limit read to size of mailbox and mbx->size is up to 15 */
if (size > mbx->size)
size = mbx->size;
/* lock the mailbox to prevent pf/vf race condition */
ret = wx_obtain_mbx_lock_vf(wx);
if (ret)
return ret;
/* copy the message from the mailbox memory buffer */
for (i = 0; i < size; i++)
msg[i] = rd32a(wx, WX_VXMBMEM, i);
/* Acknowledge receipt and release mailbox, then we're done */
wr32(wx, WX_VXMAILBOX, WX_VXMAILBOX_ACK);
return 0;
}
int wx_init_mbx_params_vf(struct wx *wx)
{
wx->vfinfo = kzalloc_obj(struct vf_data_storage);
if (!wx->vfinfo)
return -ENOMEM;
/* Initialize mailbox parameters */
wx->mbx.size = WX_VXMAILBOX_SIZE;
wx->mbx.mailbox = WX_VXMAILBOX;
wx->mbx.udelay = 10;
wx->mbx.timeout = 1000;
return 0;
}
EXPORT_SYMBOL(wx_init_mbx_params_vf);
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_MBX_H_
#define _WX_MBX_H_
#define WX_VXMAILBOX_SIZE 15
/* PF Registers */
#define WX_PXMAILBOX(i) (0x600 + (4 * (i))) /* i=[0,63] */
#define WX_PXMAILBOX_STS BIT(0) /* Initiate message send to VF */
#define WX_PXMAILBOX_ACK BIT(1) /* Ack message recv'd from VF */
#define WX_PXMAILBOX_PFU BIT(3) /* PF owns the mailbox buffer */
/* VF Registers */
#define WX_VXMAILBOX 0x600
#define WX_VXMAILBOX_REQ BIT(0) /* Request for PF Ready bit */
#define WX_VXMAILBOX_ACK BIT(1) /* Ack PF message received */
#define WX_VXMAILBOX_VFU BIT(2) /* VF owns the mailbox buffer */
#define WX_VXMAILBOX_PFU BIT(3) /* PF owns the mailbox buffer */
#define WX_VXMAILBOX_PFSTS BIT(4) /* PF wrote a message in the MB */
#define WX_VXMAILBOX_PFACK BIT(5) /* PF ack the previous VF msg */
#define WX_VXMAILBOX_RSTI BIT(6) /* PF has reset indication */
#define WX_VXMAILBOX_RSTD BIT(7) /* PF has indicated reset done */
#define WX_VXMAILBOX_R2C_BITS (WX_VXMAILBOX_RSTD | \
WX_VXMAILBOX_PFSTS | WX_VXMAILBOX_PFACK)
#define WX_VXMBMEM 0x00C00 /* 16*4B */
#define WX_PXMBMEM(i) (0x5000 + (64 * (i))) /* i=[0,63] */
#define WX_VFLRE(i) (0x4A0 + (4 * (i))) /* i=[0,1] */
#define WX_VFLREC(i) (0x4A8 + (4 * (i))) /* i=[0,1] */
/* SR-IOV specific macros */
#define WX_MBVFICR(i) (0x480 + (4 * (i))) /* i=[0,3] */
#define WX_MBVFICR_VFREQ_MASK GENMASK(15, 0)
#define WX_MBVFICR_VFACK_MASK GENMASK(31, 16)
#define WX_VT_MSGTYPE_ACK BIT(31)
#define WX_VT_MSGTYPE_NACK BIT(30)
#define WX_VT_MSGTYPE_CTS BIT(29)
#define WX_VT_MSGINFO_SHIFT 16
#define WX_VT_MSGINFO_MASK GENMASK(23, 16)
enum wx_pfvf_api_rev {
wx_mbox_api_null,
wx_mbox_api_13 = 4, /* API version 1.3 */
wx_mbox_api_unknown, /* indicates that API version is not known */
};
/* mailbox API */
#define WX_VF_RESET 0x01 /* VF requests reset */
#define WX_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */
#define WX_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */
#define WX_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */
#define WX_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */
#define WX_VF_SET_MACVLAN 0x06 /* VF requests PF unicast filter */
#define WX_VF_API_NEGOTIATE 0x08 /* negotiate API version */
#define WX_VF_GET_QUEUES 0x09 /* get queue configuration */
#define WX_VF_GET_RETA 0x0a /* VF request for RETA */
#define WX_VF_GET_RSS_KEY 0x0b /* get RSS key */
#define WX_VF_UPDATE_XCAST_MODE 0x0c
#define WX_VF_GET_LINK_STATE 0x10 /* get vf link state */
#define WX_VF_GET_FW_VERSION 0x11 /* get fw version */
#define WX_VF_BACKUP 0x8001 /* VF requests backup */
#define WX_PF_CONTROL_MSG BIT(8) /* PF control message */
#define WX_PF_NOFITY_VF_LINK_STATUS 0x1
#define WX_PF_NOFITY_VF_NET_NOT_RUNNING BIT(31)
#define WX_VF_TX_QUEUES 1 /* number of Tx queues supported */
#define WX_VF_RX_QUEUES 2 /* number of Rx queues supported */
#define WX_VF_TRANS_VLAN 3 /* Indication of port vlan */
#define WX_VF_DEF_QUEUE 4 /* Default queue offset */
#define WX_VF_PERMADDR_MSG_LEN 4
enum wxvf_xcast_modes {
WXVF_XCAST_MODE_NONE = 0,
WXVF_XCAST_MODE_MULTI,
WXVF_XCAST_MODE_ALLMULTI,
WXVF_XCAST_MODE_PROMISC,
};
int wx_write_mbx_pf(struct wx *wx, u32 *msg, u16 size, u16 vf);
int wx_read_mbx_pf(struct wx *wx, u32 *msg, u16 size, u16 vf);
int wx_check_for_rst_pf(struct wx *wx, u16 mbx_id);
int wx_check_for_msg_pf(struct wx *wx, u16 mbx_id);
int wx_check_for_ack_pf(struct wx *wx, u16 mbx_id);
int wx_read_posted_mbx(struct wx *wx, u32 *msg, u16 size);
int wx_write_posted_mbx(struct wx *wx, u32 *msg, u16 size);
int wx_check_for_rst_vf(struct wx *wx);
int wx_check_for_msg_vf(struct wx *wx);
int wx_read_mbx_vf(struct wx *wx, u32 *msg, u16 size);
int wx_write_mbx_vf(struct wx *wx, u32 *msg, u16 size);
int wx_init_mbx_params_vf(struct wx *wx);
#endif /* _WX_MBX_H_ */
@@ -0,0 +1,905 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
/* Copyright (c) 1999 - 2025 Intel Corporation. */
#include <linux/ptp_classify.h>
#include <linux/clocksource.h>
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_ptp.h"
#include "wx_hw.h"
#define WX_INCVAL_10GB 0xCCCCCC
#define WX_INCVAL_1GB 0x800000
#define WX_INCVAL_100 0xA00000
#define WX_INCVAL_10 0xC7F380
#define WX_INCVAL_EM 0x2000000
#define WX_INCVAL_AML 0xA00000
#define WX_INCVAL_SHIFT_10GB 20
#define WX_INCVAL_SHIFT_1GB 18
#define WX_INCVAL_SHIFT_100 15
#define WX_INCVAL_SHIFT_10 12
#define WX_INCVAL_SHIFT_EM 22
#define WX_INCVAL_SHIFT_AML 21
#define WX_OVERFLOW_PERIOD (HZ * 30)
#define WX_PTP_TX_TIMEOUT (HZ)
#define WX_1588_PPS_WIDTH_EM 120
#define WX_NS_PER_SEC 1000000000ULL
static u64 wx_ptp_timecounter_cyc2time(struct wx *wx, u64 timestamp)
{
unsigned int seq;
u64 ns;
do {
seq = read_seqbegin(&wx->hw_tc_lock);
ns = timecounter_cyc2time(&wx->hw_tc, timestamp);
} while (read_seqretry(&wx->hw_tc_lock, seq));
return ns;
}
static u64 wx_ptp_readtime(struct wx *wx, struct ptp_system_timestamp *sts)
{
u32 timeh1, timeh2, timel;
timeh1 = rd32ptp(wx, WX_TSC_1588_SYSTIMH);
ptp_read_system_prets(sts);
timel = rd32ptp(wx, WX_TSC_1588_SYSTIML);
ptp_read_system_postts(sts);
timeh2 = rd32ptp(wx, WX_TSC_1588_SYSTIMH);
if (timeh1 != timeh2) {
ptp_read_system_prets(sts);
timel = rd32ptp(wx, WX_TSC_1588_SYSTIML);
ptp_read_system_prets(sts);
}
return (u64)timel | (u64)timeh2 << 32;
}
static int wx_ptp_adjfine(struct ptp_clock_info *ptp, long ppb)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
u64 incval, mask;
smp_mb(); /* Force any pending update before accessing. */
incval = READ_ONCE(wx->base_incval);
incval = adjust_by_scaled_ppm(incval, ppb);
mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
incval &= mask;
if (wx->mac.type != wx_mac_em)
incval |= 2 << 24;
wr32ptp(wx, WX_TSC_1588_INC, incval);
return 0;
}
static int wx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
unsigned long flags;
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_adjtime(&wx->hw_tc, delta);
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
return 0;
}
static int wx_ptp_gettimex64(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
u64 ns, stamp;
stamp = wx_ptp_readtime(wx, sts);
ns = wx_ptp_timecounter_cyc2time(wx, stamp);
*ts = ns_to_timespec64(ns);
return 0;
}
static int wx_ptp_settime64(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
unsigned long flags;
u64 ns;
ns = timespec64_to_ns(ts);
/* reset the timecounter */
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_init(&wx->hw_tc, &wx->hw_cc, ns);
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
return 0;
}
/**
* wx_ptp_clear_tx_timestamp - utility function to clear Tx timestamp state
* @wx: the private board structure
*
* This function should be called whenever the state related to a Tx timestamp
* needs to be cleared. This helps ensure that all related bits are reset for
* the next Tx timestamp event.
*/
static void wx_ptp_clear_tx_timestamp(struct wx *wx)
{
rd32ptp(wx, WX_TSC_1588_STMPH);
if (wx->ptp_tx_skb) {
dev_kfree_skb_any(wx->ptp_tx_skb);
wx->ptp_tx_skb = NULL;
}
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
}
/**
* wx_ptp_convert_to_hwtstamp - convert register value to hw timestamp
* @wx: private board structure
* @hwtstamp: stack timestamp structure
* @timestamp: unsigned 64bit system time value
*
* We need to convert the adapter's RX/TXSTMP registers into a hwtstamp value
* which can be used by the stack's ptp functions.
*
* The lock is used to protect consistency of the cyclecounter and the SYSTIME
* registers. However, it does not need to protect against the Rx or Tx
* timestamp registers, as there can't be a new timestamp until the old one is
* unlatched by reading.
*
* In addition to the timestamp in hardware, some controllers need a software
* overflow cyclecounter, and this function takes this into account as well.
**/
static void wx_ptp_convert_to_hwtstamp(struct wx *wx,
struct skb_shared_hwtstamps *hwtstamp,
u64 timestamp)
{
u64 ns;
ns = wx_ptp_timecounter_cyc2time(wx, timestamp);
hwtstamp->hwtstamp = ns_to_ktime(ns);
}
/**
* wx_ptp_tx_hwtstamp - utility function which checks for TX time stamp
* @wx: the private board struct
*
* if the timestamp is valid, we convert it into the timecounter ns
* value, then store that result into the shhwtstamps structure which
* is passed up the network stack
*/
static void wx_ptp_tx_hwtstamp(struct wx *wx)
{
struct skb_shared_hwtstamps shhwtstamps;
struct sk_buff *skb = wx->ptp_tx_skb;
u64 regval = 0;
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPL);
regval |= (u64)rd32ptp(wx, WX_TSC_1588_STMPH) << 32;
wx_ptp_convert_to_hwtstamp(wx, &shhwtstamps, regval);
wx->ptp_tx_skb = NULL;
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
wx->tx_hwtstamp_pkts++;
}
static int wx_ptp_tx_hwtstamp_work(struct wx *wx)
{
u32 tsynctxctl;
/* we have to have a valid skb to poll for a timestamp */
if (!wx->ptp_tx_skb) {
wx_ptp_clear_tx_timestamp(wx);
return 0;
}
/* stop polling once we have a valid timestamp */
tsynctxctl = rd32ptp(wx, WX_TSC_1588_CTL);
if (tsynctxctl & WX_TSC_1588_CTL_VALID) {
wx_ptp_tx_hwtstamp(wx);
return 0;
}
return -1;
}
/**
* wx_ptp_overflow_check - watchdog task to detect SYSTIME overflow
* @wx: pointer to wx struct
*
* this watchdog task periodically reads the timecounter
* in order to prevent missing when the system time registers wrap
* around. This needs to be run approximately twice a minute for the fastest
* overflowing hardware. We run it for all hardware since it shouldn't have a
* large impact.
*/
static void wx_ptp_overflow_check(struct wx *wx)
{
bool timeout = time_is_before_jiffies(wx->last_overflow_check +
WX_OVERFLOW_PERIOD);
unsigned long flags;
if (timeout) {
/* Update the timecounter */
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_read(&wx->hw_tc);
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
wx->last_overflow_check = jiffies;
}
}
/**
* wx_ptp_rx_hang - detect error case when Rx timestamp registers latched
* @wx: pointer to wx struct
*
* this watchdog task is scheduled to detect error case where hardware has
* dropped an Rx packet that was timestamped when the ring is full. The
* particular error is rare but leaves the device in a state unable to
* timestamp any future packets.
*/
static void wx_ptp_rx_hang(struct wx *wx)
{
struct wx_ring *rx_ring;
unsigned long rx_event;
u32 tsyncrxctl;
int n;
tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
/* if we don't have a valid timestamp in the registers, just update the
* timeout counter and exit
*/
if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID)) {
wx->last_rx_ptp_check = jiffies;
return;
}
/* determine the most recent watchdog or rx_timestamp event */
rx_event = wx->last_rx_ptp_check;
for (n = 0; n < wx->num_rx_queues; n++) {
rx_ring = wx->rx_ring[n];
if (time_after(rx_ring->last_rx_timestamp, rx_event))
rx_event = rx_ring->last_rx_timestamp;
}
/* only need to read the high RXSTMP register to clear the lock */
if (time_is_before_jiffies(rx_event + 5 * HZ)) {
rd32(wx, WX_PSR_1588_STMPH);
wx->last_rx_ptp_check = jiffies;
wx->rx_hwtstamp_cleared++;
dev_warn(&wx->pdev->dev, "clearing RX Timestamp hang");
}
}
/**
* wx_ptp_tx_hang - detect error case where Tx timestamp never finishes
* @wx: private network wx structure
*/
static void wx_ptp_tx_hang(struct wx *wx)
{
bool timeout = time_is_before_jiffies(wx->ptp_tx_start +
WX_PTP_TX_TIMEOUT);
if (!wx->ptp_tx_skb)
return;
if (!test_bit(WX_STATE_PTP_TX_IN_PROGRESS, wx->state))
return;
/* If we haven't received a timestamp within the timeout, it is
* reasonable to assume that it will never occur, so we can unlock the
* timestamp bit when this occurs.
*/
if (timeout) {
wx_ptp_clear_tx_timestamp(wx);
wx->tx_hwtstamp_timeouts++;
dev_warn(&wx->pdev->dev, "clearing Tx timestamp hang\n");
}
}
static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
int ts_done;
ts_done = wx_ptp_tx_hwtstamp_work(wx);
wx_ptp_overflow_check(wx);
if (unlikely(test_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
wx->flags)))
wx_ptp_rx_hang(wx);
wx_ptp_tx_hang(wx);
return ts_done ? 1 : HZ;
}
static u64 wx_ptp_trigger_calc(struct wx *wx)
{
struct cyclecounter *cc = &wx->hw_cc;
unsigned long flags;
u64 ns = 0;
u32 rem;
/* Read the current clock time, and save the cycle counter value */
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
ns = timecounter_read(&wx->hw_tc);
wx->pps_edge_start = wx->hw_tc.cycle_last;
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
wx->pps_edge_end = wx->pps_edge_start;
/* Figure out how far past the next second we are */
div_u64_rem(ns, WX_NS_PER_SEC, &rem);
/* Figure out how many nanoseconds to add to round the clock edge up
* to the next full second
*/
rem = (WX_NS_PER_SEC - rem);
/* Adjust the clock edge to align with the next full second. */
wx->pps_edge_start += div_u64(((u64)rem << cc->shift), cc->mult);
wx->pps_edge_end += div_u64(((u64)(rem + wx->pps_width) <<
cc->shift), cc->mult);
return (ns + rem);
}
static int wx_ptp_setup_sdp(struct wx *wx)
{
struct cyclecounter *cc = &wx->hw_cc;
u32 tsauxc;
u64 nsec;
if (wx->pps_width >= WX_NS_PER_SEC) {
wx_err(wx, "PTP pps width cannot be longer than 1s!\n");
return -EINVAL;
}
/* disable the pin first */
wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
WX_WRITE_FLUSH(wx);
if (!test_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags)) {
if (wx->pps_enabled) {
wx->pps_enabled = false;
wx_set_pps(wx, false, 0, 0);
}
return 0;
}
wx->pps_enabled = true;
nsec = wx_ptp_trigger_calc(wx);
wx_set_pps(wx, wx->pps_enabled, nsec, wx->pps_edge_start);
tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
wr32ptp(wx, WX_TSC_1588_TRGT_L(0), (u32)wx->pps_edge_start);
wr32ptp(wx, WX_TSC_1588_TRGT_H(0), (u32)(wx->pps_edge_start >> 32));
wr32ptp(wx, WX_TSC_1588_TRGT_L(1), (u32)wx->pps_edge_end);
wr32ptp(wx, WX_TSC_1588_TRGT_H(1), (u32)(wx->pps_edge_end >> 32));
wr32ptp(wx, WX_TSC_1588_SDP(0),
WX_TSC_1588_SDP_FUN_SEL_TT0 | WX_TSC_1588_SDP_OUT_LEVEL_H);
wr32ptp(wx, WX_TSC_1588_SDP(1), WX_TSC_1588_SDP_FUN_SEL_TS0);
wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
wr32ptp(wx, WX_TSC_1588_INT_EN, WX_TSC_1588_INT_EN_TT1);
WX_WRITE_FLUSH(wx);
/* Adjust the clock edge to align with the next full second. */
wx->sec_to_cc = div_u64(((u64)WX_NS_PER_SEC << cc->shift), cc->mult);
return 0;
}
static int wx_ptp_feature_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
/**
* When PPS is enabled, unmask the interrupt for the ClockOut
* feature, so that the interrupt handler can send the PPS
* event when the clock SDP triggers. Clear mask when PPS is
* disabled
*/
if (rq->type != PTP_CLK_REQ_PEROUT || !wx->ptp_setup_sdp)
return -EOPNOTSUPP;
/* Reject requests with unsupported flags */
if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
PTP_PEROUT_PHASE))
return -EOPNOTSUPP;
if (rq->perout.phase.sec || rq->perout.phase.nsec) {
wx_err(wx, "Absolute start time not supported.\n");
return -EINVAL;
}
if (rq->perout.period.sec != 1 || rq->perout.period.nsec) {
wx_err(wx, "Only 1pps is supported.\n");
return -EINVAL;
}
if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
struct timespec64 ts_on;
ts_on.tv_sec = rq->perout.on.sec;
ts_on.tv_nsec = rq->perout.on.nsec;
wx->pps_width = timespec64_to_ns(&ts_on);
} else {
wx->pps_width = 120000000;
}
if (on)
set_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
else
clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
return wx->ptp_setup_sdp(wx);
}
void wx_ptp_check_pps_event(struct wx *wx)
{
u32 tsauxc, int_status;
/* this check is necessary in case the interrupt was enabled via some
* alternative means (ex. debug_fs). Better to check here than
* everywhere that calls this function.
*/
if (!wx->ptp_clock)
return;
int_status = rd32ptp(wx, WX_TSC_1588_INT_ST);
if (int_status & WX_TSC_1588_INT_ST_TT1) {
/* disable the pin first */
wr32ptp(wx, WX_TSC_1588_AUX_CTL, 0);
WX_WRITE_FLUSH(wx);
wx_ptp_trigger_calc(wx);
tsauxc = WX_TSC_1588_AUX_CTL_PLSG | WX_TSC_1588_AUX_CTL_EN_TT0 |
WX_TSC_1588_AUX_CTL_EN_TT1 | WX_TSC_1588_AUX_CTL_EN_TS0;
wr32ptp(wx, WX_TSC_1588_TRGT_L(0), (u32)wx->pps_edge_start);
wr32ptp(wx, WX_TSC_1588_TRGT_H(0), (u32)(wx->pps_edge_start >> 32));
wr32ptp(wx, WX_TSC_1588_TRGT_L(1), (u32)wx->pps_edge_end);
wr32ptp(wx, WX_TSC_1588_TRGT_H(1), (u32)(wx->pps_edge_end >> 32));
wr32ptp(wx, WX_TSC_1588_AUX_CTL, tsauxc);
WX_WRITE_FLUSH(wx);
}
}
EXPORT_SYMBOL(wx_ptp_check_pps_event);
static long wx_ptp_create_clock(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
long err;
/* do nothing if we already have a clock device */
if (!IS_ERR_OR_NULL(wx->ptp_clock))
return 0;
snprintf(wx->ptp_caps.name, sizeof(wx->ptp_caps.name),
"%s", netdev->name);
wx->ptp_caps.owner = THIS_MODULE;
wx->ptp_caps.n_alarm = 0;
wx->ptp_caps.n_ext_ts = 0;
wx->ptp_caps.pps = 0;
wx->ptp_caps.adjfine = wx_ptp_adjfine;
wx->ptp_caps.adjtime = wx_ptp_adjtime;
wx->ptp_caps.gettimex64 = wx_ptp_gettimex64;
wx->ptp_caps.settime64 = wx_ptp_settime64;
wx->ptp_caps.do_aux_work = wx_ptp_do_aux_work;
switch (wx->mac.type) {
case wx_mac_aml:
case wx_mac_aml40:
wx->ptp_caps.max_adj = 250000000;
wx->ptp_caps.n_per_out = 1;
wx->ptp_setup_sdp = wx_ptp_setup_sdp;
wx->ptp_caps.enable = wx_ptp_feature_enable;
break;
case wx_mac_sp:
wx->ptp_caps.max_adj = 250000000;
wx->ptp_caps.n_per_out = 0;
wx->ptp_setup_sdp = NULL;
break;
case wx_mac_em:
wx->ptp_caps.max_adj = 500000000;
wx->ptp_caps.n_per_out = 1;
wx->ptp_setup_sdp = wx_ptp_setup_sdp;
wx->ptp_caps.enable = wx_ptp_feature_enable;
break;
default:
return -EOPNOTSUPP;
}
wx->ptp_clock = ptp_clock_register(&wx->ptp_caps, &wx->pdev->dev);
if (IS_ERR(wx->ptp_clock)) {
err = PTR_ERR(wx->ptp_clock);
wx->ptp_clock = NULL;
wx_err(wx, "ptp clock register failed\n");
return err;
} else if (wx->ptp_clock) {
dev_info(&wx->pdev->dev, "registered PHC device on %s\n",
netdev->name);
}
/* Set the default timestamp mode to disabled here. We do this in
* create_clock instead of initialization, because we don't want to
* override the previous settings during a suspend/resume cycle.
*/
wx->tstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
wx->tstamp_config.tx_type = HWTSTAMP_TX_OFF;
return 0;
}
static int wx_ptp_set_timestamp_mode(struct wx *wx,
struct kernel_hwtstamp_config *config)
{
u32 tsync_tx_ctl = WX_TSC_1588_CTL_ENABLED;
u32 tsync_rx_ctl = WX_PSR_1588_CTL_ENABLED;
DECLARE_BITMAP(flags, WX_PF_FLAGS_NBITS);
u32 tsync_rx_mtrl = PTP_EV_PORT << 16;
bool is_l2 = false;
u32 regval;
memcpy(flags, wx->flags, sizeof(wx->flags));
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
tsync_tx_ctl = 0;
break;
case HWTSTAMP_TX_ON:
break;
default:
return -ERANGE;
}
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
tsync_rx_ctl = 0;
tsync_rx_mtrl = 0;
clear_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
clear_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_SYNC;
set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_L4_V1;
tsync_rx_mtrl |= WX_PSR_1588_MSG_V1_DELAY_REQ;
set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
tsync_rx_ctl |= WX_PSR_1588_CTL_TYPE_EVENT_V2;
is_l2 = true;
config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
set_bit(WX_FLAG_RX_HWTSTAMP_ENABLED, flags);
set_bit(WX_FLAG_RX_HWTSTAMP_IN_REGISTER, flags);
break;
default:
/* register PSR_1588_MSG must be set in order to do V1 packets,
* therefore it is not possible to time stamp both V1 Sync and
* Delay_Req messages unless hardware supports timestamping all
* packets => return error
*/
config->rx_filter = HWTSTAMP_FILTER_NONE;
return -ERANGE;
}
/* define ethertype filter for timestamping L2 packets */
if (is_l2)
wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588),
(WX_PSR_ETYPE_SWC_FILTER_EN | /* enable filter */
WX_PSR_ETYPE_SWC_1588 | /* enable timestamping */
ETH_P_1588)); /* 1588 eth protocol type */
else
wr32(wx, WX_PSR_ETYPE_SWC(WX_PSR_ETYPE_SWC_FILTER_1588), 0);
/* enable/disable TX */
regval = rd32ptp(wx, WX_TSC_1588_CTL);
regval &= ~WX_TSC_1588_CTL_ENABLED;
regval |= tsync_tx_ctl;
wr32ptp(wx, WX_TSC_1588_CTL, regval);
/* enable/disable RX */
regval = rd32(wx, WX_PSR_1588_CTL);
regval &= ~(WX_PSR_1588_CTL_ENABLED | WX_PSR_1588_CTL_TYPE_MASK);
regval |= tsync_rx_ctl;
wr32(wx, WX_PSR_1588_CTL, regval);
/* define which PTP packets are time stamped */
wr32(wx, WX_PSR_1588_MSG, tsync_rx_mtrl);
WX_WRITE_FLUSH(wx);
/* configure adapter flags only when HW is actually configured */
memcpy(wx->flags, flags, sizeof(wx->flags));
/* clear TX/RX timestamp state, just to be sure */
wx_ptp_clear_tx_timestamp(wx);
rd32(wx, WX_PSR_1588_STMPH);
return 0;
}
static u64 wx_ptp_read(struct cyclecounter *hw_cc)
{
struct wx *wx = container_of(hw_cc, struct wx, hw_cc);
return wx_ptp_readtime(wx, NULL);
}
static void wx_ptp_link_speed_adjust(struct wx *wx, u32 *shift, u32 *incval)
{
switch (wx->mac.type) {
case wx_mac_aml:
case wx_mac_aml40:
*shift = WX_INCVAL_SHIFT_AML;
*incval = WX_INCVAL_AML;
return;
case wx_mac_em:
*shift = WX_INCVAL_SHIFT_EM;
*incval = WX_INCVAL_EM;
return;
default:
break;
}
switch (wx->speed) {
case SPEED_10:
*shift = WX_INCVAL_SHIFT_10;
*incval = WX_INCVAL_10;
break;
case SPEED_100:
*shift = WX_INCVAL_SHIFT_100;
*incval = WX_INCVAL_100;
break;
case SPEED_1000:
*shift = WX_INCVAL_SHIFT_1GB;
*incval = WX_INCVAL_1GB;
break;
case SPEED_10000:
default:
*shift = WX_INCVAL_SHIFT_10GB;
*incval = WX_INCVAL_10GB;
break;
}
}
/**
* wx_ptp_reset_cyclecounter - create the cycle counter from hw
* @wx: pointer to the wx structure
*
* This function should be called to set the proper values for the TSC_1588_INC
* register and tell the cyclecounter structure what the tick rate of SYSTIME
* is. It does not directly modify SYSTIME registers or the timecounter
* structure. It should be called whenever a new TSC_1588_INC value is
* necessary, such as during initialization or when the link speed changes.
*/
void wx_ptp_reset_cyclecounter(struct wx *wx)
{
u32 incval = 0, mask = 0;
struct cyclecounter cc;
unsigned long flags;
/* For some of the boards below this mask is technically incorrect.
* The timestamp mask overflows at approximately 61bits. However the
* particular hardware does not overflow on an even bitmask value.
* Instead, it overflows due to conversion of upper 32bits billions of
* cycles. Timecounters are not really intended for this purpose so
* they do not properly function if the overflow point isn't 2^N-1.
* However, the actual SYSTIME values in question take ~138 years to
* overflow. In practice this means they won't actually overflow. A
* proper fix to this problem would require modification of the
* timecounter delta calculations.
*/
cc.mask = CLOCKSOURCE_MASK(64);
cc.mult = 1;
cc.shift = 0;
cc.read = wx_ptp_read;
wx_ptp_link_speed_adjust(wx, &cc.shift, &incval);
/* update the base incval used to calculate frequency adjustment */
WRITE_ONCE(wx->base_incval, incval);
mask = (wx->mac.type == wx_mac_em) ? 0x7FFFFFF : 0xFFFFFF;
incval &= mask;
if (wx->mac.type != wx_mac_em)
incval |= 2 << 24;
wr32ptp(wx, WX_TSC_1588_INC, incval);
smp_mb(); /* Force the above update. */
/* need lock to prevent incorrect read while modifying cyclecounter */
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
memcpy(&wx->hw_cc, &cc, sizeof(wx->hw_cc));
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
}
EXPORT_SYMBOL(wx_ptp_reset_cyclecounter);
void wx_ptp_reset(struct wx *wx)
{
unsigned long flags;
/* reset the hardware timestamping mode */
wx_ptp_set_timestamp_mode(wx, &wx->tstamp_config);
wx_ptp_reset_cyclecounter(wx);
wr32ptp(wx, WX_TSC_1588_SYSTIML, 0);
wr32ptp(wx, WX_TSC_1588_SYSTIMH, 0);
WX_WRITE_FLUSH(wx);
write_seqlock_irqsave(&wx->hw_tc_lock, flags);
timecounter_init(&wx->hw_tc, &wx->hw_cc,
ktime_to_ns(ktime_get_real()));
write_sequnlock_irqrestore(&wx->hw_tc_lock, flags);
wx->last_overflow_check = jiffies;
ptp_schedule_worker(wx->ptp_clock, HZ);
/* Now that the shift has been calculated and the systime
* registers reset, (re-)enable the Clock out feature
*/
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
}
EXPORT_SYMBOL(wx_ptp_reset);
void wx_ptp_init(struct wx *wx)
{
/* Initialize the seqlock_t first, since the user might call the clock
* functions any time after we've initialized the ptp clock device.
*/
seqlock_init(&wx->hw_tc_lock);
/* obtain a ptp clock device, or re-use an existing device */
if (wx_ptp_create_clock(wx))
return;
wx->tx_hwtstamp_pkts = 0;
wx->tx_hwtstamp_timeouts = 0;
wx->tx_hwtstamp_skipped = 0;
wx->tx_hwtstamp_errors = 0;
wx->rx_hwtstamp_cleared = 0;
/* reset the ptp related hardware bits */
wx_ptp_reset(wx);
/* enter the WX_STATE_PTP_RUNNING state */
set_bit(WX_STATE_PTP_RUNNING, wx->state);
}
EXPORT_SYMBOL(wx_ptp_init);
/**
* wx_ptp_suspend - stop ptp work items
* @wx: pointer to wx struct
*
* This function suspends ptp activity, and prevents more work from being
* generated, but does not destroy the clock device.
*/
void wx_ptp_suspend(struct wx *wx)
{
/* leave the WX_STATE_PTP_RUNNING STATE */
if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
return;
clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
if (wx->ptp_setup_sdp)
wx->ptp_setup_sdp(wx);
wx_ptp_clear_tx_timestamp(wx);
}
EXPORT_SYMBOL(wx_ptp_suspend);
/**
* wx_ptp_stop - destroy the ptp_clock device
* @wx: pointer to wx struct
*
* Completely destroy the ptp_clock device, and disable all PTP related
* features. Intended to be run when the device is being closed.
*/
void wx_ptp_stop(struct wx *wx)
{
/* first, suspend ptp activity */
wx_ptp_suspend(wx);
/* now destroy the ptp clock device */
if (wx->ptp_clock) {
ptp_clock_unregister(wx->ptp_clock);
wx->ptp_clock = NULL;
dev_info(&wx->pdev->dev, "removed PHC on %s\n", wx->netdev->name);
}
}
EXPORT_SYMBOL(wx_ptp_stop);
/**
* wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp
* @wx: pointer to wx struct
* @skb: particular skb to send timestamp with
*
* if the timestamp is valid, we convert it into the timecounter ns
* value, then store that result into the shhwtstamps structure which
* is passed up the network stack
*/
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb)
{
u64 regval = 0;
u32 tsyncrxctl;
/* Read the tsyncrxctl register afterwards in order to prevent taking an
* I/O hit on every packet.
*/
tsyncrxctl = rd32(wx, WX_PSR_1588_CTL);
if (!(tsyncrxctl & WX_PSR_1588_CTL_VALID))
return;
regval |= (u64)rd32(wx, WX_PSR_1588_STMPL);
regval |= (u64)rd32(wx, WX_PSR_1588_STMPH) << 32;
wx_ptp_convert_to_hwtstamp(wx, skb_hwtstamps(skb), regval);
}
int wx_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg)
{
struct wx *wx = netdev_priv(dev);
if (!netif_running(dev))
return -EINVAL;
*cfg = wx->tstamp_config;
return 0;
}
EXPORT_SYMBOL(wx_hwtstamp_get);
int wx_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct wx *wx = netdev_priv(dev);
int err;
if (!netif_running(dev))
return -EINVAL;
err = wx_ptp_set_timestamp_mode(wx, cfg);
if (err)
return err;
/* save these settings for future reference */
memcpy(&wx->tstamp_config, cfg, sizeof(wx->tstamp_config));
return 0;
}
EXPORT_SYMBOL(wx_hwtstamp_set);
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2019 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_PTP_H_
#define _WX_PTP_H_
void wx_ptp_check_pps_event(struct wx *wx);
void wx_ptp_reset_cyclecounter(struct wx *wx);
void wx_ptp_reset(struct wx *wx);
void wx_ptp_init(struct wx *wx);
void wx_ptp_suspend(struct wx *wx);
void wx_ptp_stop(struct wx *wx);
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
int wx_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg);
int wx_hwtstamp_set(struct net_device *dev,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack);
#endif /* _WX_PTP_H_ */
@@ -0,0 +1,927 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_hw.h"
#include "wx_mbx.h"
#include "wx_sriov.h"
static void wx_vf_configuration(struct pci_dev *pdev, int event_mask)
{
bool enable = !!WX_VF_ENABLE_CHECK(event_mask);
struct wx *wx = pci_get_drvdata(pdev);
u32 vfn = WX_VF_NUM_GET(event_mask);
if (enable)
eth_zero_addr(wx->vfinfo[vfn].vf_mac_addr);
}
static int wx_alloc_vf_macvlans(struct wx *wx, u8 num_vfs)
{
struct vf_macvlans *mv_list;
int num_vf_macvlans, i;
/* Initialize list of VF macvlans */
INIT_LIST_HEAD(&wx->vf_mvs.mvlist);
num_vf_macvlans = wx->mac.num_rar_entries -
(WX_MAX_PF_MACVLANS + 1 + num_vfs);
if (!num_vf_macvlans)
return -EINVAL;
mv_list = kzalloc_objs(struct vf_macvlans, num_vf_macvlans);
if (!mv_list)
return -ENOMEM;
for (i = 0; i < num_vf_macvlans; i++) {
mv_list[i].vf = -1;
mv_list[i].free = true;
list_add(&mv_list[i].mvlist, &wx->vf_mvs.mvlist);
}
wx->mv_list = mv_list;
return 0;
}
static void wx_sriov_clear_data(struct wx *wx)
{
/* set num VFs to 0 to prevent access to vfinfo */
wx->num_vfs = 0;
/* free VF control structures */
kfree(wx->vfinfo);
wx->vfinfo = NULL;
/* free macvlan list */
kfree(wx->mv_list);
wx->mv_list = NULL;
/* set default pool back to 0 */
wr32m(wx, WX_PSR_VM_CTL, WX_PSR_VM_CTL_POOL_MASK, 0);
wx->ring_feature[RING_F_VMDQ].offset = 0;
clear_bit(WX_FLAG_IRQ_VECTOR_SHARED, wx->flags);
clear_bit(WX_FLAG_SRIOV_ENABLED, wx->flags);
/* Disable VMDq flag so device will be set in NM mode */
if (wx->ring_feature[RING_F_VMDQ].limit == 1)
clear_bit(WX_FLAG_VMDQ_ENABLED, wx->flags);
}
static int __wx_enable_sriov(struct wx *wx, u8 num_vfs)
{
int i, ret = 0;
u32 value = 0;
set_bit(WX_FLAG_SRIOV_ENABLED, wx->flags);
dev_info(&wx->pdev->dev, "SR-IOV enabled with %d VFs\n", num_vfs);
if (num_vfs == 7 && wx->mac.type == wx_mac_em)
set_bit(WX_FLAG_IRQ_VECTOR_SHARED, wx->flags);
/* Enable VMDq flag so device will be set in VM mode */
set_bit(WX_FLAG_VMDQ_ENABLED, wx->flags);
if (!wx->ring_feature[RING_F_VMDQ].limit)
wx->ring_feature[RING_F_VMDQ].limit = 1;
wx->ring_feature[RING_F_VMDQ].offset = num_vfs;
wx->vfinfo = kzalloc_objs(struct vf_data_storage, num_vfs);
if (!wx->vfinfo)
return -ENOMEM;
ret = wx_alloc_vf_macvlans(wx, num_vfs);
if (ret)
return ret;
/* Initialize default switching mode VEB */
wr32m(wx, WX_PSR_CTL, WX_PSR_CTL_SW_EN, WX_PSR_CTL_SW_EN);
for (i = 0; i < num_vfs; i++) {
/* enable spoof checking for all VFs */
wx->vfinfo[i].spoofchk_enabled = true;
wx->vfinfo[i].link_enable = true;
/* untrust all VFs */
wx->vfinfo[i].trusted = false;
/* set the default xcast mode */
wx->vfinfo[i].xcast_mode = WXVF_XCAST_MODE_NONE;
}
if (!test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) {
value = WX_CFG_PORT_CTL_NUM_VT_8;
} else {
if (num_vfs < 32)
value = WX_CFG_PORT_CTL_NUM_VT_32;
else
value = WX_CFG_PORT_CTL_NUM_VT_64;
}
wr32m(wx, WX_CFG_PORT_CTL,
WX_CFG_PORT_CTL_NUM_VT_MASK,
value);
/* Disable RSC when in SR-IOV mode */
clear_bit(WX_FLAG_RSC_CAPABLE, wx->flags);
clear_bit(WX_FLAG_RSC_ENABLED, wx->flags);
return ret;
}
static void wx_sriov_reinit(struct wx *wx)
{
rtnl_lock();
wx->setup_tc(wx->netdev, netdev_get_num_tc(wx->netdev));
rtnl_unlock();
}
void wx_disable_sriov(struct wx *wx)
{
if (!pci_vfs_assigned(wx->pdev))
pci_disable_sriov(wx->pdev);
else
wx_err(wx, "Unloading driver while VFs are assigned.\n");
/* clear flags and free allloced data */
wx_sriov_clear_data(wx);
}
EXPORT_SYMBOL(wx_disable_sriov);
static int wx_pci_sriov_enable(struct pci_dev *dev,
int num_vfs)
{
struct wx *wx = pci_get_drvdata(dev);
int err = 0, i;
if (netif_is_rxfh_configured(wx->netdev)) {
wx_err(wx, "Cannot enable SR-IOV while RXFH is configured\n");
wx_err(wx, "Run 'ethtool -X <if> default' to reset RSS table\n");
return -EBUSY;
}
err = __wx_enable_sriov(wx, num_vfs);
if (err)
return err;
wx->num_vfs = num_vfs;
for (i = 0; i < wx->num_vfs; i++)
wx_vf_configuration(dev, (i | WX_VF_ENABLE));
/* reset before enabling SRIOV to avoid mailbox issues */
wx_sriov_reinit(wx);
err = pci_enable_sriov(dev, num_vfs);
if (err) {
wx_err(wx, "Failed to enable PCI sriov: %d\n", err);
goto err_out;
}
return num_vfs;
err_out:
wx_sriov_clear_data(wx);
return err;
}
static int wx_pci_sriov_disable(struct pci_dev *dev)
{
struct wx *wx = pci_get_drvdata(dev);
if (netif_is_rxfh_configured(wx->netdev)) {
wx_err(wx, "Cannot disable SR-IOV while RXFH is configured\n");
wx_err(wx, "Run 'ethtool -X <if> default' to reset RSS table\n");
return -EBUSY;
}
wx_disable_sriov(wx);
wx_sriov_reinit(wx);
return 0;
}
int wx_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
struct wx *wx = pci_get_drvdata(pdev);
int err;
if (!num_vfs) {
if (!pci_vfs_assigned(pdev))
return wx_pci_sriov_disable(pdev);
wx_err(wx, "can't free VFs because some are assigned to VMs.\n");
return -EBUSY;
}
err = wx_pci_sriov_enable(pdev, num_vfs);
if (err)
return err;
return num_vfs;
}
EXPORT_SYMBOL(wx_pci_sriov_configure);
static int wx_set_vf_mac(struct wx *wx, u16 vf, const u8 *mac_addr)
{
u8 hw_addr[ETH_ALEN];
int ret = 0;
ether_addr_copy(hw_addr, mac_addr);
wx_del_mac_filter(wx, wx->vfinfo[vf].vf_mac_addr, vf);
ret = wx_add_mac_filter(wx, hw_addr, vf);
if (ret >= 0)
ether_addr_copy(wx->vfinfo[vf].vf_mac_addr, mac_addr);
else
eth_zero_addr(wx->vfinfo[vf].vf_mac_addr);
return ret;
}
static void wx_set_vmolr(struct wx *wx, u16 vf, bool aupe)
{
u32 vmolr = rd32(wx, WX_PSR_VM_L2CTL(vf));
vmolr |= WX_PSR_VM_L2CTL_BAM;
if (aupe)
vmolr |= WX_PSR_VM_L2CTL_AUPE;
else
vmolr &= ~WX_PSR_VM_L2CTL_AUPE;
wr32(wx, WX_PSR_VM_L2CTL(vf), vmolr);
}
static void wx_set_vmvir(struct wx *wx, u16 vid, u16 qos, u16 vf)
{
u32 vmvir = vid | (qos << VLAN_PRIO_SHIFT) |
WX_TDM_VLAN_INS_VLANA_DEFAULT;
wr32(wx, WX_TDM_VLAN_INS(vf), vmvir);
}
static int wx_set_vf_vlan(struct wx *wx, int add, int vid, u16 vf)
{
if (!vid && !add)
return 0;
return wx_set_vfta(wx, vid, vf, (bool)add);
}
static void wx_set_vlan_anti_spoofing(struct wx *wx, bool enable, int vf)
{
u32 index = WX_VF_REG_OFFSET(vf), vf_bit = WX_VF_IND_SHIFT(vf);
u32 pfvfspoof;
pfvfspoof = rd32(wx, WX_TDM_VLAN_AS(index));
if (enable)
pfvfspoof |= BIT(vf_bit);
else
pfvfspoof &= ~BIT(vf_bit);
wr32(wx, WX_TDM_VLAN_AS(index), pfvfspoof);
}
static void wx_write_qde(struct wx *wx, u32 vf, u32 qde)
{
struct wx_ring_feature *vmdq = &wx->ring_feature[RING_F_VMDQ];
u32 q_per_pool = __ALIGN_MASK(1, ~vmdq->mask);
u32 reg = 0, n = vf * q_per_pool / 32;
u32 i = vf * q_per_pool;
reg = rd32(wx, WX_RDM_PF_QDE(n));
for (i = (vf * q_per_pool - n * 32);
i < ((vf + 1) * q_per_pool - n * 32);
i++) {
if (qde == 1)
reg |= qde << i;
else
reg &= qde << i;
}
wr32(wx, WX_RDM_PF_QDE(n), reg);
}
static void wx_clear_vmvir(struct wx *wx, u32 vf)
{
wr32(wx, WX_TDM_VLAN_INS(vf), 0);
}
static void wx_ping_vf(struct wx *wx, int vf)
{
u32 ping = WX_PF_CONTROL_MSG;
if (wx->vfinfo[vf].clear_to_send)
ping |= WX_VT_MSGTYPE_CTS;
wx_write_mbx_pf(wx, &ping, 1, vf);
}
static void wx_set_vf_rx_tx(struct wx *wx, int vf)
{
u32 index = WX_VF_REG_OFFSET(vf), vf_bit = WX_VF_IND_SHIFT(vf);
u32 reg_cur_tx, reg_cur_rx, reg_req_tx, reg_req_rx;
reg_cur_tx = rd32(wx, WX_TDM_VF_TE(index));
reg_cur_rx = rd32(wx, WX_RDM_VF_RE(index));
if (wx->vfinfo[vf].link_enable) {
reg_req_tx = reg_cur_tx | BIT(vf_bit);
reg_req_rx = reg_cur_rx | BIT(vf_bit);
/* Enable particular VF */
if (reg_cur_tx != reg_req_tx)
wr32(wx, WX_TDM_VF_TE(index), reg_req_tx);
if (reg_cur_rx != reg_req_rx)
wr32(wx, WX_RDM_VF_RE(index), reg_req_rx);
} else {
reg_req_tx = BIT(vf_bit);
reg_req_rx = BIT(vf_bit);
/* Disable particular VF */
if (reg_cur_tx & reg_req_tx)
wr32(wx, WX_TDM_VFTE_CLR(index), reg_req_tx);
if (reg_cur_rx & reg_req_rx)
wr32(wx, WX_RDM_VFRE_CLR(index), reg_req_rx);
}
}
static int wx_get_vf_queues(struct wx *wx, u32 *msgbuf, u32 vf)
{
struct wx_ring_feature *vmdq = &wx->ring_feature[RING_F_VMDQ];
unsigned int default_tc = 0;
msgbuf[WX_VF_TX_QUEUES] = __ALIGN_MASK(1, ~vmdq->mask);
msgbuf[WX_VF_RX_QUEUES] = __ALIGN_MASK(1, ~vmdq->mask);
if (wx->vfinfo[vf].pf_vlan || wx->vfinfo[vf].pf_qos)
msgbuf[WX_VF_TRANS_VLAN] = 1;
else
msgbuf[WX_VF_TRANS_VLAN] = 0;
/* notify VF of default queue */
msgbuf[WX_VF_DEF_QUEUE] = default_tc;
return 0;
}
static void wx_vf_reset_event(struct wx *wx, u16 vf)
{
struct vf_data_storage *vfinfo = &wx->vfinfo[vf];
u8 num_tcs = netdev_get_num_tc(wx->netdev);
/* add PF assigned VLAN */
wx_set_vf_vlan(wx, true, vfinfo->pf_vlan, vf);
/* reset offloads to defaults */
wx_set_vmolr(wx, vf, !vfinfo->pf_vlan);
/* set outgoing tags for VFs */
if (!vfinfo->pf_vlan && !vfinfo->pf_qos && !num_tcs) {
wx_clear_vmvir(wx, vf);
} else {
if (vfinfo->pf_qos || !num_tcs)
wx_set_vmvir(wx, vfinfo->pf_vlan,
vfinfo->pf_qos, vf);
else
wx_set_vmvir(wx, vfinfo->pf_vlan,
wx->default_up, vf);
}
/* reset multicast table array for vf */
wx->vfinfo[vf].num_vf_mc_hashes = 0;
/* Flush and reset the mta with the new values */
wx_set_rx_mode(wx->netdev);
wx_del_mac_filter(wx, wx->vfinfo[vf].vf_mac_addr, vf);
/* reset VF api back to unknown */
wx->vfinfo[vf].vf_api = wx_mbox_api_null;
}
static void wx_vf_reset_msg(struct wx *wx, u16 vf)
{
const u8 *vf_mac = wx->vfinfo[vf].vf_mac_addr;
struct net_device *dev = wx->netdev;
u32 msgbuf[5] = {0, 0, 0, 0, 0};
u8 *addr = (u8 *)(&msgbuf[1]);
u32 reg = 0, index, vf_bit;
int pf_max_frame;
/* reset the filters for the device */
wx_vf_reset_event(wx, vf);
/* set vf mac address */
if (!is_zero_ether_addr(vf_mac))
wx_set_vf_mac(wx, vf, vf_mac);
index = WX_VF_REG_OFFSET(vf);
vf_bit = WX_VF_IND_SHIFT(vf);
/* force drop enable for all VF Rx queues */
wx_write_qde(wx, vf, 1);
/* set transmit and receive for vf */
wx_set_vf_rx_tx(wx, vf);
pf_max_frame = dev->mtu + ETH_HLEN;
if (pf_max_frame > ETH_FRAME_LEN)
reg = BIT(vf_bit);
wr32(wx, WX_RDM_VFRE_CLR(index), reg);
/* enable VF mailbox for further messages */
wx->vfinfo[vf].clear_to_send = true;
/* reply to reset with ack and vf mac address */
msgbuf[0] = WX_VF_RESET;
if (!is_zero_ether_addr(vf_mac)) {
msgbuf[0] |= WX_VT_MSGTYPE_ACK;
memcpy(addr, vf_mac, ETH_ALEN);
} else {
msgbuf[0] |= WX_VT_MSGTYPE_NACK;
wx_err(wx, "VF %d has no MAC address assigned", vf);
}
msgbuf[3] = wx->mac.mc_filter_type;
wx_write_mbx_pf(wx, msgbuf, WX_VF_PERMADDR_MSG_LEN, vf);
}
static int wx_set_vf_mac_addr(struct wx *wx, u32 *msgbuf, u16 vf)
{
const u8 *new_mac = ((u8 *)(&msgbuf[1]));
int ret;
if (!is_valid_ether_addr(new_mac)) {
wx_err(wx, "VF %d attempted to set invalid mac\n", vf);
return -EINVAL;
}
if (wx->vfinfo[vf].pf_set_mac &&
memcmp(wx->vfinfo[vf].vf_mac_addr, new_mac, ETH_ALEN)) {
wx_err(wx,
"VF %d attempt to set a MAC but it already had a MAC.",
vf);
return -EBUSY;
}
ret = wx_set_vf_mac(wx, vf, new_mac);
if (ret < 0)
return ret;
return 0;
}
static void wx_set_vf_multicasts(struct wx *wx, u32 *msgbuf, u32 vf)
{
struct vf_data_storage *vfinfo = &wx->vfinfo[vf];
u16 entries = (msgbuf[0] & WX_VT_MSGINFO_MASK)
>> WX_VT_MSGINFO_SHIFT;
u32 vmolr = rd32(wx, WX_PSR_VM_L2CTL(vf));
u32 vector_bit, vector_reg, mta_reg, i;
u16 *hash_list = (u16 *)&msgbuf[1];
/* only so many hash values supported */
entries = min_t(u16, entries, WX_MAX_VF_MC_ENTRIES);
vfinfo->num_vf_mc_hashes = entries;
for (i = 0; i < entries; i++)
vfinfo->vf_mc_hashes[i] = hash_list[i];
for (i = 0; i < vfinfo->num_vf_mc_hashes; i++) {
vector_reg = WX_PSR_MC_TBL_REG(vfinfo->vf_mc_hashes[i]);
vector_bit = WX_PSR_MC_TBL_BIT(vfinfo->vf_mc_hashes[i]);
mta_reg = wx->mac.mta_shadow[vector_reg];
mta_reg |= BIT(vector_bit);
wx->mac.mta_shadow[vector_reg] = mta_reg;
wr32(wx, WX_PSR_MC_TBL(vector_reg), mta_reg);
}
vmolr |= WX_PSR_VM_L2CTL_ROMPE;
wr32(wx, WX_PSR_VM_L2CTL(vf), vmolr);
}
static void wx_set_vf_lpe(struct wx *wx, u32 max_frame, u32 vf)
{
u32 index, vf_bit, vfre;
u32 max_frs, reg_val;
/* determine VF receive enable location */
index = WX_VF_REG_OFFSET(vf);
vf_bit = WX_VF_IND_SHIFT(vf);
vfre = rd32(wx, WX_RDM_VF_RE(index));
vfre |= BIT(vf_bit);
wr32(wx, WX_RDM_VF_RE(index), vfre);
/* pull current max frame size from hardware */
max_frs = DIV_ROUND_UP(max_frame, 1024);
reg_val = rd32(wx, WX_MAC_WDG_TIMEOUT) & WX_MAC_WDG_TIMEOUT_WTO_MASK;
if (max_frs > (reg_val + WX_MAC_WDG_TIMEOUT_WTO_DELTA))
wr32(wx, WX_MAC_WDG_TIMEOUT,
max_frs - WX_MAC_WDG_TIMEOUT_WTO_DELTA);
}
static int wx_find_vlvf_entry(struct wx *wx, u32 vlan)
{
int regindex;
u32 vlvf;
/* short cut the special case */
if (vlan == 0)
return 0;
/* Search for the vlan id in the VLVF entries */
for (regindex = 1; regindex < WX_PSR_VLAN_SWC_ENTRIES; regindex++) {
wr32(wx, WX_PSR_VLAN_SWC_IDX, regindex);
vlvf = rd32(wx, WX_PSR_VLAN_SWC);
if ((vlvf & VLAN_VID_MASK) == vlan)
break;
}
/* Return a negative value if not found */
if (regindex >= WX_PSR_VLAN_SWC_ENTRIES)
regindex = -EINVAL;
return regindex;
}
static int wx_set_vf_macvlan(struct wx *wx,
u16 vf, int index, unsigned char *mac_addr)
{
struct vf_macvlans *entry;
struct list_head *pos;
int retval = 0;
if (index <= 1) {
list_for_each(pos, &wx->vf_mvs.mvlist) {
entry = list_entry(pos, struct vf_macvlans, mvlist);
if (entry->vf == vf) {
entry->vf = -1;
entry->free = true;
entry->is_macvlan = false;
wx_del_mac_filter(wx, entry->vf_macvlan, vf);
}
}
}
if (!index)
return 0;
entry = NULL;
list_for_each(pos, &wx->vf_mvs.mvlist) {
entry = list_entry(pos, struct vf_macvlans, mvlist);
if (entry->free)
break;
}
if (!entry || !entry->free)
return -ENOSPC;
retval = wx_add_mac_filter(wx, mac_addr, vf);
if (retval >= 0) {
entry->free = false;
entry->is_macvlan = true;
entry->vf = vf;
memcpy(entry->vf_macvlan, mac_addr, ETH_ALEN);
}
return retval;
}
static int wx_set_vf_vlan_msg(struct wx *wx, u32 *msgbuf, u16 vf)
{
int add = (msgbuf[0] & WX_VT_MSGINFO_MASK) >> WX_VT_MSGINFO_SHIFT;
int vid = (msgbuf[1] & WX_PSR_VLAN_SWC_VLANID_MASK);
int ret;
if (add)
wx->vfinfo[vf].vlan_count++;
else if (wx->vfinfo[vf].vlan_count)
wx->vfinfo[vf].vlan_count--;
/* in case of promiscuous mode any VLAN filter set for a VF must
* also have the PF pool added to it.
*/
if (add && wx->netdev->flags & IFF_PROMISC)
wx_set_vf_vlan(wx, add, vid, VMDQ_P(0));
ret = wx_set_vf_vlan(wx, add, vid, vf);
if (!ret && wx->vfinfo[vf].spoofchk_enabled)
wx_set_vlan_anti_spoofing(wx, true, vf);
/* Go through all the checks to see if the VLAN filter should
* be wiped completely.
*/
if (!add && wx->netdev->flags & IFF_PROMISC) {
u32 bits = 0, vlvf;
int reg_ndx;
reg_ndx = wx_find_vlvf_entry(wx, vid);
if (reg_ndx < 0)
return -ENOSPC;
wr32(wx, WX_PSR_VLAN_SWC_IDX, reg_ndx);
vlvf = rd32(wx, WX_PSR_VLAN_SWC);
/* See if any other pools are set for this VLAN filter
* entry other than the PF.
*/
if (VMDQ_P(0) < 32) {
bits = rd32(wx, WX_PSR_VLAN_SWC_VM_L);
bits &= ~BIT(VMDQ_P(0));
if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags))
bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_H);
} else {
if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags))
bits = rd32(wx, WX_PSR_VLAN_SWC_VM_H);
bits &= ~BIT(VMDQ_P(0) % 32);
bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_L);
}
/* If the filter was removed then ensure PF pool bit
* is cleared if the PF only added itself to the pool
* because the PF is in promiscuous mode.
*/
if ((vlvf & VLAN_VID_MASK) == vid && !bits)
wx_set_vf_vlan(wx, add, vid, VMDQ_P(0));
}
return 0;
}
static int wx_set_vf_macvlan_msg(struct wx *wx, u32 *msgbuf, u16 vf)
{
int index = (msgbuf[0] & WX_VT_MSGINFO_MASK) >>
WX_VT_MSGINFO_SHIFT;
u8 *new_mac = ((u8 *)(&msgbuf[1]));
int err;
if (wx->vfinfo[vf].pf_set_mac && index > 0) {
wx_err(wx, "VF %d request MACVLAN filter but is denied\n", vf);
return -EINVAL;
}
/* An non-zero index indicates the VF is setting a filter */
if (index) {
if (!is_valid_ether_addr(new_mac)) {
wx_err(wx, "VF %d attempted to set invalid mac\n", vf);
return -EINVAL;
}
/* If the VF is allowed to set MAC filters then turn off
* anti-spoofing to avoid false positives.
*/
if (wx->vfinfo[vf].spoofchk_enabled)
wx_set_vf_spoofchk(wx->netdev, vf, false);
}
err = wx_set_vf_macvlan(wx, vf, index, new_mac);
if (err == -ENOSPC)
wx_err(wx,
"VF %d request MACVLAN filter but there is no space\n",
vf);
if (err < 0)
return err;
return 0;
}
static int wx_negotiate_vf_api(struct wx *wx, u32 *msgbuf, u32 vf)
{
int api = msgbuf[1];
switch (api) {
case wx_mbox_api_13:
wx->vfinfo[vf].vf_api = api;
return 0;
default:
wx_err(wx, "VF %d requested invalid api version %u\n", vf, api);
return -EINVAL;
}
}
static int wx_get_vf_link_state(struct wx *wx, u32 *msgbuf, u32 vf)
{
msgbuf[1] = wx->vfinfo[vf].link_enable;
return 0;
}
static int wx_get_fw_version(struct wx *wx, u32 *msgbuf, u32 vf)
{
unsigned long fw_version = 0ULL;
int ret = 0;
ret = kstrtoul(wx->eeprom_id, 16, &fw_version);
if (ret)
return -EOPNOTSUPP;
msgbuf[1] = fw_version;
return 0;
}
static int wx_update_vf_xcast_mode(struct wx *wx, u32 *msgbuf, u32 vf)
{
int xcast_mode = msgbuf[1];
u32 vmolr, disable, enable;
if (wx->vfinfo[vf].xcast_mode == xcast_mode)
return 0;
switch (xcast_mode) {
case WXVF_XCAST_MODE_NONE:
disable = WX_PSR_VM_L2CTL_BAM | WX_PSR_VM_L2CTL_ROMPE |
WX_PSR_VM_L2CTL_MPE | WX_PSR_VM_L2CTL_UPE |
WX_PSR_VM_L2CTL_VPE;
enable = 0;
break;
case WXVF_XCAST_MODE_MULTI:
disable = WX_PSR_VM_L2CTL_MPE | WX_PSR_VM_L2CTL_UPE |
WX_PSR_VM_L2CTL_VPE;
enable = WX_PSR_VM_L2CTL_BAM | WX_PSR_VM_L2CTL_ROMPE;
break;
case WXVF_XCAST_MODE_ALLMULTI:
disable = WX_PSR_VM_L2CTL_UPE | WX_PSR_VM_L2CTL_VPE;
enable = WX_PSR_VM_L2CTL_BAM | WX_PSR_VM_L2CTL_ROMPE |
WX_PSR_VM_L2CTL_MPE;
break;
case WXVF_XCAST_MODE_PROMISC:
disable = 0;
enable = WX_PSR_VM_L2CTL_BAM | WX_PSR_VM_L2CTL_ROMPE |
WX_PSR_VM_L2CTL_MPE | WX_PSR_VM_L2CTL_UPE |
WX_PSR_VM_L2CTL_VPE;
break;
default:
return -EOPNOTSUPP;
}
vmolr = rd32(wx, WX_PSR_VM_L2CTL(vf));
vmolr &= ~disable;
vmolr |= enable;
wr32(wx, WX_PSR_VM_L2CTL(vf), vmolr);
wx->vfinfo[vf].xcast_mode = xcast_mode;
msgbuf[1] = xcast_mode;
return 0;
}
static void wx_rcv_msg_from_vf(struct wx *wx, u16 vf)
{
u16 mbx_size = WX_VXMAILBOX_SIZE;
u32 msgbuf[WX_VXMAILBOX_SIZE];
int retval;
retval = wx_read_mbx_pf(wx, msgbuf, mbx_size, vf);
if (retval) {
wx_err(wx, "Error receiving message from VF\n");
return;
}
/* this is a message we already processed, do nothing */
if (msgbuf[0] & (WX_VT_MSGTYPE_ACK | WX_VT_MSGTYPE_NACK))
return;
if (msgbuf[0] == WX_VF_RESET) {
wx_vf_reset_msg(wx, vf);
return;
}
/* until the vf completes a virtual function reset it should not be
* allowed to start any configuration.
*/
if (!wx->vfinfo[vf].clear_to_send) {
msgbuf[0] |= WX_VT_MSGTYPE_NACK;
wx_write_mbx_pf(wx, msgbuf, 1, vf);
return;
}
switch ((msgbuf[0] & U16_MAX)) {
case WX_VF_SET_MAC_ADDR:
retval = wx_set_vf_mac_addr(wx, msgbuf, vf);
break;
case WX_VF_SET_MULTICAST:
wx_set_vf_multicasts(wx, msgbuf, vf);
retval = 0;
break;
case WX_VF_SET_VLAN:
retval = wx_set_vf_vlan_msg(wx, msgbuf, vf);
break;
case WX_VF_SET_LPE:
wx_set_vf_lpe(wx, msgbuf[1], vf);
retval = 0;
break;
case WX_VF_SET_MACVLAN:
retval = wx_set_vf_macvlan_msg(wx, msgbuf, vf);
break;
case WX_VF_API_NEGOTIATE:
retval = wx_negotiate_vf_api(wx, msgbuf, vf);
break;
case WX_VF_GET_QUEUES:
retval = wx_get_vf_queues(wx, msgbuf, vf);
break;
case WX_VF_GET_LINK_STATE:
retval = wx_get_vf_link_state(wx, msgbuf, vf);
break;
case WX_VF_GET_FW_VERSION:
retval = wx_get_fw_version(wx, msgbuf, vf);
break;
case WX_VF_UPDATE_XCAST_MODE:
retval = wx_update_vf_xcast_mode(wx, msgbuf, vf);
break;
case WX_VF_BACKUP:
break;
default:
wx_err(wx, "Unhandled Msg %8.8x\n", msgbuf[0]);
break;
}
/* notify the VF of the results of what it sent us */
if (retval)
msgbuf[0] |= WX_VT_MSGTYPE_NACK;
else
msgbuf[0] |= WX_VT_MSGTYPE_ACK;
msgbuf[0] |= WX_VT_MSGTYPE_CTS;
wx_write_mbx_pf(wx, msgbuf, mbx_size, vf);
}
static void wx_rcv_ack_from_vf(struct wx *wx, u16 vf)
{
u32 msg = WX_VT_MSGTYPE_NACK;
/* if device isn't clear to send it shouldn't be reading either */
if (!wx->vfinfo[vf].clear_to_send)
wx_write_mbx_pf(wx, &msg, 1, vf);
}
void wx_msg_task(struct wx *wx)
{
u16 vf;
for (vf = 0; vf < wx->num_vfs; vf++) {
/* process any reset requests */
if (!wx_check_for_rst_pf(wx, vf))
wx_vf_reset_event(wx, vf);
/* process any messages pending */
if (!wx_check_for_msg_pf(wx, vf))
wx_rcv_msg_from_vf(wx, vf);
/* process any acks */
if (!wx_check_for_ack_pf(wx, vf))
wx_rcv_ack_from_vf(wx, vf);
}
}
EXPORT_SYMBOL(wx_msg_task);
void wx_disable_vf_rx_tx(struct wx *wx)
{
wr32(wx, WX_TDM_VFTE_CLR(0), U32_MAX);
wr32(wx, WX_RDM_VFRE_CLR(0), U32_MAX);
if (test_bit(WX_FLAG_MULTI_64_FUNC, wx->flags)) {
wr32(wx, WX_TDM_VFTE_CLR(1), U32_MAX);
wr32(wx, WX_RDM_VFRE_CLR(1), U32_MAX);
}
}
EXPORT_SYMBOL(wx_disable_vf_rx_tx);
void wx_ping_all_vfs_with_link_status(struct wx *wx, bool link_up)
{
u32 msgbuf[2] = {0, 0};
u16 i;
if (!wx->num_vfs)
return;
msgbuf[0] = WX_PF_NOFITY_VF_LINK_STATUS | WX_PF_CONTROL_MSG;
if (link_up)
msgbuf[1] = FIELD_PREP(GENMASK(31, 1), wx->speed) | link_up;
if (wx->notify_down)
msgbuf[1] |= WX_PF_NOFITY_VF_NET_NOT_RUNNING;
for (i = 0; i < wx->num_vfs; i++) {
if (wx->vfinfo[i].clear_to_send)
msgbuf[0] |= WX_VT_MSGTYPE_CTS;
wx_write_mbx_pf(wx, msgbuf, 2, i);
}
}
EXPORT_SYMBOL(wx_ping_all_vfs_with_link_status);
static void wx_set_vf_link_state(struct wx *wx, int vf, int state)
{
wx->vfinfo[vf].link_state = state;
switch (state) {
case IFLA_VF_LINK_STATE_AUTO:
if (!test_bit(WX_STATE_DOWN, wx->state))
wx->vfinfo[vf].link_enable = true;
else
wx->vfinfo[vf].link_enable = false;
break;
case IFLA_VF_LINK_STATE_ENABLE:
wx->vfinfo[vf].link_enable = true;
break;
case IFLA_VF_LINK_STATE_DISABLE:
wx->vfinfo[vf].link_enable = false;
break;
}
/* restart the VF */
wx->vfinfo[vf].clear_to_send = false;
wx_ping_vf(wx, vf);
wx_set_vf_rx_tx(wx, vf);
}
void wx_set_all_vfs(struct wx *wx)
{
int i;
for (i = 0; i < wx->num_vfs; i++)
wx_set_vf_link_state(wx, i, wx->vfinfo[i].link_state);
}
EXPORT_SYMBOL(wx_set_all_vfs);
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_SRIOV_H_
#define _WX_SRIOV_H_
#define WX_VF_ENABLE_CHECK(_m) FIELD_GET(BIT(31), (_m))
#define WX_VF_NUM_GET(_m) FIELD_GET(GENMASK(5, 0), (_m))
#define WX_VF_ENABLE BIT(31)
void wx_disable_sriov(struct wx *wx);
int wx_pci_sriov_configure(struct pci_dev *pdev, int num_vfs);
void wx_msg_task(struct wx *wx);
void wx_disable_vf_rx_tx(struct wx *wx);
void wx_ping_all_vfs_with_link_status(struct wx *wx, bool link_up);
void wx_set_all_vfs(struct wx *wx);
#endif /* _WX_SRIOV_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,599 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_hw.h"
#include "wx_mbx.h"
#include "wx_vf.h"
static void wx_virt_clr_reg(struct wx *wx)
{
u32 vfsrrctl, i;
/* VRSRRCTL default values (BSIZEPACKET = 2048, BSIZEHEADER = 256) */
vfsrrctl = WX_VXRXDCTL_HDRSZ(wx_hdr_sz(WX_RX_HDR_SIZE));
vfsrrctl |= WX_VXRXDCTL_BUFSZ(wx_buf_sz(WX_RX_BUF_SIZE));
/* clear all rxd ctl */
for (i = 0; i < WX_VF_MAX_RING_NUMS; i++)
wr32m(wx, WX_VXRXDCTL(i),
WX_VXRXDCTL_HDRSZ_MASK | WX_VXRXDCTL_BUFSZ_MASK,
vfsrrctl);
rd32(wx, WX_VXSTATUS);
}
/**
* wx_init_hw_vf - virtual function hardware initialization
* @wx: pointer to hardware structure
*
* Initialize the mac address
**/
void wx_init_hw_vf(struct wx *wx)
{
wx_get_mac_addr_vf(wx, wx->mac.addr);
}
EXPORT_SYMBOL(wx_init_hw_vf);
static int wx_mbx_write_and_read_reply(struct wx *wx, u32 *req_buf,
u32 *resp_buf, u16 size)
{
int ret;
ret = wx_write_posted_mbx(wx, req_buf, size);
if (ret)
return ret;
return wx_read_posted_mbx(wx, resp_buf, size);
}
/**
* wx_reset_hw_vf - Performs hardware reset
* @wx: pointer to hardware structure
*
* Resets the hardware by resetting the transmit and receive units, masks and
* clears all interrupts.
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_reset_hw_vf(struct wx *wx)
{
struct wx_mbx_info *mbx = &wx->mbx;
u32 msgbuf[4] = {WX_VF_RESET};
u8 *addr = (u8 *)(&msgbuf[1]);
u32 b4_buf[16] = {0};
u32 timeout = 200;
int ret;
u32 i;
/* Call wx stop to disable tx/rx and clear interrupts */
wx_stop_adapter_vf(wx);
/* reset the api version */
wx->vfinfo->vf_api = wx_mbox_api_null;
/* backup msix vectors */
if (wx->b4_addr) {
for (i = 0; i < 16; i++)
b4_buf[i] = readl(wx->b4_addr + i * 4);
}
wr32m(wx, WX_VXCTRL, WX_VXCTRL_RST, WX_VXCTRL_RST);
rd32(wx, WX_VXSTATUS);
/* we cannot reset while the RSTI / RSTD bits are asserted */
while (!wx_check_for_rst_vf(wx) && timeout) {
timeout--;
udelay(5);
}
/* restore msix vectors */
if (wx->b4_addr) {
for (i = 0; i < 16; i++)
writel(b4_buf[i], wx->b4_addr + i * 4);
}
/* amlite: bme */
if (wx->mac.type == wx_mac_aml || wx->mac.type == wx_mac_aml40)
wr32(wx, WX_VX_PF_BME, WX_VF_BME_ENABLE);
if (!timeout)
return -EBUSY;
/* Reset VF registers to initial values */
wx_virt_clr_reg(wx);
/* mailbox timeout can now become active */
mbx->timeout = 2000;
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
if (msgbuf[0] != (WX_VF_RESET | WX_VT_MSGTYPE_ACK) &&
msgbuf[0] != (WX_VF_RESET | WX_VT_MSGTYPE_NACK))
return -EINVAL;
if (msgbuf[0] == (WX_VF_RESET | WX_VT_MSGTYPE_ACK))
ether_addr_copy(wx->mac.perm_addr, addr);
wx->mac.mc_filter_type = msgbuf[3];
return 0;
}
EXPORT_SYMBOL(wx_reset_hw_vf);
/**
* wx_stop_adapter_vf - Generic stop Tx/Rx units
* @wx: pointer to hardware structure
*
* Clears interrupts, disables transmit and receive units.
**/
void wx_stop_adapter_vf(struct wx *wx)
{
u32 reg_val;
u16 i;
/* Clear interrupt mask to stop from interrupts being generated */
wr32(wx, WX_VXIMS, WX_VF_IRQ_CLEAR_MASK);
/* Clear any pending interrupts, flush previous writes */
wr32(wx, WX_VXICR, U32_MAX);
/* Disable the transmit unit. Each queue must be disabled. */
for (i = 0; i < wx->mac.max_tx_queues; i++)
wr32(wx, WX_VXTXDCTL(i), WX_VXTXDCTL_FLUSH);
/* Disable the receive unit by stopping each queue */
for (i = 0; i < wx->mac.max_rx_queues; i++) {
reg_val = rd32(wx, WX_VXRXDCTL(i));
reg_val &= ~WX_VXRXDCTL_ENABLE;
wr32(wx, WX_VXRXDCTL(i), reg_val);
}
/* Clear packet split and pool config */
wr32(wx, WX_VXMRQC, 0);
/* flush all queues disables */
rd32(wx, WX_VXSTATUS);
}
EXPORT_SYMBOL(wx_stop_adapter_vf);
/**
* wx_set_rar_vf - set device MAC address
* @wx: pointer to hardware structure
* @index: Receive address register to write
* @addr: Address to put into receive address register
* @enable_addr: set flag that address is active
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_set_rar_vf(struct wx *wx, u32 index, u8 *addr, u32 enable_addr)
{
u32 msgbuf[3] = {WX_VF_SET_MAC_ADDR};
u8 *msg_addr = (u8 *)(&msgbuf[1]);
int ret;
memcpy(msg_addr, addr, ETH_ALEN);
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;
/* if nacked the address was rejected, use "perm_addr" */
if (msgbuf[0] == (WX_VF_SET_MAC_ADDR | WX_VT_MSGTYPE_NACK)) {
wx_get_mac_addr_vf(wx, wx->mac.addr);
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(wx_set_rar_vf);
/**
* wx_update_mc_addr_list_vf - Update Multicast addresses
* @wx: pointer to the HW structure
* @netdev: pointer to the net device structure
*
* Updates the Multicast Table Array.
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_update_mc_addr_list_vf(struct wx *wx, struct net_device *netdev)
{
u32 msgbuf[WX_VXMAILBOX_SIZE] = {WX_VF_SET_MULTICAST};
u16 *vector_l = (u16 *)&msgbuf[1];
struct netdev_hw_addr *ha;
u32 cnt, i;
cnt = netdev_mc_count(netdev);
if (cnt > 28)
cnt = 28;
msgbuf[0] |= cnt << WX_VT_MSGINFO_SHIFT;
i = 0;
netdev_for_each_mc_addr(ha, netdev) {
if (i == cnt)
break;
if (is_link_local_ether_addr(ha->addr))
continue;
vector_l[i++] = wx_mta_vector(wx, ha->addr);
}
return wx_write_posted_mbx(wx, msgbuf, ARRAY_SIZE(msgbuf));
}
EXPORT_SYMBOL(wx_update_mc_addr_list_vf);
/**
* wx_update_xcast_mode_vf - Update Multicast mode
* @wx: pointer to the HW structure
* @xcast_mode: new multicast mode
*
* Updates the Multicast Mode of VF.
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_update_xcast_mode_vf(struct wx *wx, int xcast_mode)
{
u32 msgbuf[2] = {WX_VF_UPDATE_XCAST_MODE, xcast_mode};
int ret = 0;
if (wx->vfinfo->vf_api < wx_mbox_api_13)
return -EINVAL;
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;
if (msgbuf[0] == (WX_VF_UPDATE_XCAST_MODE | WX_VT_MSGTYPE_NACK))
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(wx_update_xcast_mode_vf);
/**
* wx_get_link_state_vf - Get VF link state from PF
* @wx: pointer to the HW structure
* @link_state: link state storage
*
* Return: return state of the operation error or success.
**/
int wx_get_link_state_vf(struct wx *wx, u16 *link_state)
{
u32 msgbuf[2] = {WX_VF_GET_LINK_STATE};
int ret;
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
return -EINVAL;
*link_state = msgbuf[1];
return 0;
}
EXPORT_SYMBOL(wx_get_link_state_vf);
/**
* wx_set_vfta_vf - Set/Unset vlan filter table address
* @wx: pointer to the HW structure
* @vlan: 12 bit VLAN ID
* @vind: unused by VF drivers
* @vlan_on: if true then set bit, else clear bit
* @vlvf_bypass: boolean flag indicating updating default pool is okay
*
* Turn on/off specified VLAN in the VLAN filter table.
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_set_vfta_vf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on,
bool vlvf_bypass)
{
u32 msgbuf[2] = {WX_VF_SET_VLAN, vlan};
bool vlan_offload = false;
int ret;
/* Setting the 8 bit field MSG INFO to TRUE indicates "add" */
msgbuf[0] |= vlan_on << WX_VT_MSGINFO_SHIFT;
/* if vf vlan offload is disabled, allow to create vlan under pf port vlan */
msgbuf[0] |= BIT(vlan_offload);
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
if (msgbuf[0] & WX_VT_MSGTYPE_ACK)
return 0;
return msgbuf[0] & WX_VT_MSGTYPE_NACK;
}
EXPORT_SYMBOL(wx_set_vfta_vf);
void wx_get_mac_addr_vf(struct wx *wx, u8 *mac_addr)
{
ether_addr_copy(mac_addr, wx->mac.perm_addr);
}
EXPORT_SYMBOL(wx_get_mac_addr_vf);
int wx_get_fw_version_vf(struct wx *wx)
{
u32 msgbuf[2] = {WX_VF_GET_FW_VERSION};
int ret;
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
return -EINVAL;
snprintf(wx->eeprom_id, 32, "0x%08x", msgbuf[1]);
return 0;
}
EXPORT_SYMBOL(wx_get_fw_version_vf);
int wx_set_uc_addr_vf(struct wx *wx, u32 index, u8 *addr)
{
u32 msgbuf[3] = {WX_VF_SET_MACVLAN};
u8 *msg_addr = (u8 *)(&msgbuf[1]);
int ret;
/* If index is one then this is the start of a new list and needs
* indication to the PF so it can do it's own list management.
* If it is zero then that tells the PF to just clear all of
* this VF's macvlans and there is no new list.
*/
msgbuf[0] |= index << WX_VT_MSGINFO_SHIFT;
if (addr)
memcpy(msg_addr, addr, 6);
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;
if (msgbuf[0] == (WX_VF_SET_MACVLAN | WX_VT_MSGTYPE_NACK))
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(wx_set_uc_addr_vf);
/**
* wx_rlpml_set_vf - Set the maximum receive packet length
* @wx: pointer to the HW structure
* @max_size: value to assign to max frame size
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_rlpml_set_vf(struct wx *wx, u16 max_size)
{
u32 msgbuf[2] = {WX_VF_SET_LPE, max_size};
int ret;
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
if ((msgbuf[0] & WX_VF_SET_LPE) &&
(msgbuf[0] & WX_VT_MSGTYPE_NACK))
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(wx_rlpml_set_vf);
/**
* wx_negotiate_api_version - Negotiate supported API version
* @wx: pointer to the HW structure
* @api: integer containing requested API version
*
* Return: returns 0 on success, negative error code on failure
**/
int wx_negotiate_api_version(struct wx *wx, int api)
{
u32 msgbuf[2] = {WX_VF_API_NEGOTIATE, api};
int ret;
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;
/* Store value and return 0 on success */
if (msgbuf[0] == (WX_VF_API_NEGOTIATE | WX_VT_MSGTYPE_NACK))
return -EINVAL;
wx->vfinfo->vf_api = api;
return 0;
}
EXPORT_SYMBOL(wx_negotiate_api_version);
int wx_get_queues_vf(struct wx *wx, u32 *num_tcs, u32 *default_tc)
{
u32 msgbuf[5] = {WX_VF_GET_QUEUES};
int ret;
/* do nothing if API doesn't support wx_get_queues */
if (wx->vfinfo->vf_api < wx_mbox_api_13)
return -EINVAL;
/* Fetch queue configuration from the PF */
ret = wx_mbx_write_and_read_reply(wx, msgbuf, msgbuf,
ARRAY_SIZE(msgbuf));
if (ret)
return ret;
msgbuf[0] &= ~WX_VT_MSGTYPE_CTS;
/* if we didn't get an ACK there must have been
* some sort of mailbox error so we should treat it
* as such
*/
if (msgbuf[0] != (WX_VF_GET_QUEUES | WX_VT_MSGTYPE_ACK))
return -EINVAL;
/* record and validate values from message */
wx->mac.max_tx_queues = msgbuf[WX_VF_TX_QUEUES];
if (wx->mac.max_tx_queues == 0 ||
wx->mac.max_tx_queues > WX_VF_MAX_TX_QUEUES)
wx->mac.max_tx_queues = WX_VF_MAX_TX_QUEUES;
wx->mac.max_rx_queues = msgbuf[WX_VF_RX_QUEUES];
if (wx->mac.max_rx_queues == 0 ||
wx->mac.max_rx_queues > WX_VF_MAX_RX_QUEUES)
wx->mac.max_rx_queues = WX_VF_MAX_RX_QUEUES;
*num_tcs = msgbuf[WX_VF_TRANS_VLAN];
/* in case of unknown state assume we cannot tag frames */
if (*num_tcs > wx->mac.max_rx_queues)
*num_tcs = 1;
*default_tc = msgbuf[WX_VF_DEF_QUEUE];
/* default to queue 0 on out-of-bounds queue number */
if (*default_tc >= wx->mac.max_tx_queues)
*default_tc = 0;
return 0;
}
EXPORT_SYMBOL(wx_get_queues_vf);
static int wx_get_link_status_from_pf(struct wx *wx, u32 *msgbuf)
{
u32 links_reg = msgbuf[1];
if (msgbuf[1] & WX_PF_NOFITY_VF_NET_NOT_RUNNING)
wx->notify_down = true;
else
wx->notify_down = false;
if (wx->notify_down) {
wx->link = false;
wx->speed = SPEED_UNKNOWN;
return 0;
}
wx->link = WX_PFLINK_STATUS(links_reg);
wx->speed = WX_PFLINK_SPEED(links_reg);
return 0;
}
static int wx_pf_ping_vf(struct wx *wx, u32 *msgbuf)
{
if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS))
/* msg is not CTS, we need to do reset */
return -EINVAL;
return 0;
}
static struct wx_link_reg_fields wx_speed_lookup_vf[] = {
{wx_mac_unknown},
{wx_mac_sp, SPEED_10000, SPEED_1000, SPEED_100, SPEED_UNKNOWN, SPEED_UNKNOWN},
{wx_mac_em, SPEED_1000, SPEED_100, SPEED_10, SPEED_UNKNOWN, SPEED_UNKNOWN},
{wx_mac_aml, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN},
{wx_mac_aml40, SPEED_40000, SPEED_25000, SPEED_10000, SPEED_1000, SPEED_UNKNOWN},
};
static void wx_check_physical_link(struct wx *wx)
{
u32 val, link_val;
int ret;
/* get link status from hw status reg
* for SFP+ modules and DA cables, it can take up to 500usecs
* before the link status is correct
*/
if (wx->mac.type == wx_mac_em)
ret = read_poll_timeout_atomic(rd32, val, val & GENMASK(4, 1),
100, 500, false, wx, WX_VXSTATUS);
else
ret = read_poll_timeout_atomic(rd32, val, val & BIT(0), 100,
500, false, wx, WX_VXSTATUS);
if (ret) {
wx->speed = SPEED_UNKNOWN;
wx->link = false;
return;
}
wx->link = true;
link_val = WX_VXSTATUS_SPEED(val);
if (link_val & BIT(0))
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit0_f;
else if (link_val & BIT(1))
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit1_f;
else if (link_val & BIT(2))
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit2_f;
else if (link_val & BIT(3))
wx->speed = wx_speed_lookup_vf[wx->mac.type].bit3_f;
else
wx->speed = SPEED_UNKNOWN;
}
int wx_check_mac_link_vf(struct wx *wx)
{
struct wx_mbx_info *mbx = &wx->mbx;
u32 msgbuf[2] = {0};
int ret = 0;
if (!mbx->timeout)
goto out;
wx_check_for_rst_vf(wx);
if (!wx_check_for_msg_vf(wx))
ret = wx_read_mbx_vf(wx, msgbuf, 2);
if (ret)
goto out;
switch (msgbuf[0] & GENMASK(8, 0)) {
case WX_PF_NOFITY_VF_LINK_STATUS | WX_PF_CONTROL_MSG:
ret = wx_get_link_status_from_pf(wx, msgbuf);
goto out;
case WX_PF_CONTROL_MSG:
ret = wx_pf_ping_vf(wx, msgbuf);
goto out;
case 0:
if (msgbuf[0] & WX_VT_MSGTYPE_NACK) {
/* msg is NACK, we must have lost CTS status */
ret = -EBUSY;
goto out;
}
/* no message, check link status */
wx_check_physical_link(wx);
goto out;
default:
break;
}
if (!(msgbuf[0] & WX_VT_MSGTYPE_CTS)) {
/* msg is not CTS and is NACK we must have lost CTS status */
if (msgbuf[0] & WX_VT_MSGTYPE_NACK)
ret = -EBUSY;
goto out;
}
/* the pf is talking, if we timed out in the past we reinit */
if (!mbx->timeout) {
ret = -EBUSY;
goto out;
}
out:
return ret;
}
@@ -0,0 +1,129 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_VF_H_
#define _WX_VF_H_
/* Control registers */
#define WX_VF_MAX_RING_NUMS 8
#define WX_VX_PF_BME 0x4B8
#define WX_VF_BME_ENABLE BIT(0)
#define WX_VXSTATUS 0x4
#define WX_VXCTRL 0x8
#define WX_VXCTRL_RST BIT(0)
#define WX_VXMRQC 0x78
#define WX_VXMRQC_PSR_L4HDR BIT(0)
#define WX_VXMRQC_PSR_L3HDR BIT(1)
#define WX_VXMRQC_PSR_L2HDR BIT(2)
#define WX_VXMRQC_PSR_TUNHDR BIT(3)
#define WX_VXMRQC_PSR_TUNMAC BIT(4)
#define WX_VXMRQC_PSR_MASK GENMASK(5, 1)
#define WX_VXMRQC_PSR(f) FIELD_PREP(GENMASK(5, 1), f)
#define WX_VXMRQC_RSS_HASH(f) FIELD_PREP(GENMASK(15, 13), f)
#define WX_VXMRQC_RSS_MASK GENMASK(31, 16)
#define WX_VXMRQC_RSS(f) FIELD_PREP(GENMASK(31, 16), f)
#define WX_VXMRQC_RSS_ALG_IPV4_TCP BIT(0)
#define WX_VXMRQC_RSS_ALG_IPV4 BIT(1)
#define WX_VXMRQC_RSS_ALG_IPV6 BIT(4)
#define WX_VXMRQC_RSS_ALG_IPV6_TCP BIT(5)
#define WX_VXMRQC_RSS_EN BIT(8)
#define WX_VXRSSRK(i) (0x80 + ((i) * 4)) /* i=[0,9] */
#define WX_VXRETA(i) (0xC0 + ((i) * 4)) /* i=[0,15] */
/* Interrupt registers */
#define WX_VXICR 0x100
#define WX_VXIMS 0x108
#define WX_VXIMC 0x10C
#define WX_VF_IRQ_CLEAR_MASK 7
#define WX_VF_MAX_TX_QUEUES 4
#define WX_VF_MAX_RX_QUEUES 4
#define WX_VXITR(i) (0x200 + (4 * (i))) /* i=[0,1] */
#define WX_VXITR_MASK GENMASK(8, 0)
#define WX_VXITR_CNT_WDIS BIT(31)
#define WX_VXIVAR_MISC 0x260
#define WX_VXIVAR(i) (0x240 + (4 * (i))) /* i=[0,3] */
#define wx_conf_size(v, mwidth, uwidth) ({ \
typeof(v) _v = (v); \
(_v == 2 << (mwidth) ? 0 : _v >> (uwidth)); \
})
#define wx_buf_len(v) wx_conf_size(v, 13, 7)
#define wx_hdr_sz(v) wx_conf_size(v, 10, 6)
#define wx_buf_sz(v) wx_conf_size(v, 14, 10)
#define wx_pkt_thresh(v) wx_conf_size(v, 4, 0)
#define WX_RX_HDR_SIZE 256
#define WX_RX_BUF_SIZE 2048
#define WX_RXBUFFER_2048 (2048)
#define WX_RXBUFFER_3072 3072
/* Receive Path */
#define WX_VXRDBAL(r) (0x1000 + (0x40 * (r)))
#define WX_VXRDBAH(r) (0x1004 + (0x40 * (r)))
#define WX_VXRDT(r) (0x1008 + (0x40 * (r)))
#define WX_VXRDH(r) (0x100C + (0x40 * (r)))
#define WX_VXRXDCTL(r) (0x1010 + (0x40 * (r)))
#define WX_VXRXDCTL_ENABLE BIT(0)
#define WX_VXRXDCTL_BUFLEN_MASK GENMASK(6, 1)
#define WX_VXRXDCTL_BUFLEN(f) FIELD_PREP(GENMASK(6, 1), f)
#define WX_VXRXDCTL_BUFSZ_MASK GENMASK(11, 8)
#define WX_VXRXDCTL_BUFSZ(f) FIELD_PREP(GENMASK(11, 8), f)
#define WX_VXRXDCTL_HDRSZ_MASK GENMASK(15, 12)
#define WX_VXRXDCTL_HDRSZ(f) FIELD_PREP(GENMASK(15, 12), f)
#define WX_VXRXDCTL_DESC_MERGE BIT(19)
#define WX_VXRXDCTL_RSCMAX_MASK GENMASK(24, 23)
#define WX_VXRXDCTL_RSCMAX(f) FIELD_PREP(GENMASK(24, 23), f)
#define WX_VXRXDCTL_RSCEN BIT(29)
#define WX_VXRXDCTL_DROP BIT(30)
#define WX_VXRXDCTL_VLAN BIT(31)
/* Transimit Path */
#define WX_VXTDBAL(r) (0x3000 + (0x40 * (r)))
#define WX_VXTDBAH(r) (0x3004 + (0x40 * (r)))
#define WX_VXTDT(r) (0x3008 + (0x40 * (r)))
#define WX_VXTDH(r) (0x300C + (0x40 * (r)))
#define WX_VXTXDCTL(r) (0x3010 + (0x40 * (r)))
#define WX_VXTXDCTL_ENABLE BIT(0)
#define WX_VXTXDCTL_BUFLEN(f) FIELD_PREP(GENMASK(6, 1), f)
#define WX_VXTXDCTL_PTHRESH(f) FIELD_PREP(GENMASK(11, 8), f)
#define WX_VXTXDCTL_WTHRESH(f) FIELD_PREP(GENMASK(22, 16), f)
#define WX_VXTXDCTL_FLUSH BIT(26)
#define WX_VXTXDCTL_HEAD_WB BIT(27)
#define WX_VXTXD_HEAD_ADDRL(r) (0x3028 + (0x40 * (r)))
#define WX_VXTXD_HEAD_ADDRH(r) (0x302C + (0x40 * (r)))
#define WX_PFLINK_STATUS(g) FIELD_GET(BIT(0), g)
#define WX_PFLINK_SPEED(g) FIELD_GET(GENMASK(31, 1), g)
#define WX_VXSTATUS_SPEED(g) FIELD_GET(GENMASK(4, 1), g)
struct wx_link_reg_fields {
u32 mac_type;
u32 bit0_f;
u32 bit1_f;
u32 bit2_f;
u32 bit3_f;
u32 bit4_f;
};
void wx_init_hw_vf(struct wx *wx);
int wx_reset_hw_vf(struct wx *wx);
void wx_get_mac_addr_vf(struct wx *wx, u8 *mac_addr);
void wx_stop_adapter_vf(struct wx *wx);
int wx_get_fw_version_vf(struct wx *wx);
int wx_set_rar_vf(struct wx *wx, u32 index, u8 *addr, u32 enable_addr);
int wx_update_mc_addr_list_vf(struct wx *wx, struct net_device *netdev);
int wx_set_uc_addr_vf(struct wx *wx, u32 index, u8 *addr);
int wx_rlpml_set_vf(struct wx *wx, u16 max_size);
int wx_negotiate_api_version(struct wx *wx, int api);
int wx_get_queues_vf(struct wx *wx, u32 *num_tcs, u32 *default_tc);
int wx_update_xcast_mode_vf(struct wx *wx, int xcast_mode);
int wx_get_link_state_vf(struct wx *wx, u16 *link_state);
int wx_set_vfta_vf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on,
bool vlvf_bypass);
int wx_check_mac_link_vf(struct wx *wx);
#endif /* _WX_VF_H_ */
@@ -0,0 +1,421 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_mbx.h"
#include "wx_lib.h"
#include "wx_vf.h"
#include "wx_vf_lib.h"
#include "wx_vf_common.h"
int wxvf_suspend(struct device *dev_d)
{
struct pci_dev *pdev = to_pci_dev(dev_d);
struct wx *wx = pci_get_drvdata(pdev);
netif_device_detach(wx->netdev);
wx_clear_interrupt_scheme(wx);
pci_disable_device(pdev);
return 0;
}
EXPORT_SYMBOL(wxvf_suspend);
void wxvf_shutdown(struct pci_dev *pdev)
{
wxvf_suspend(&pdev->dev);
}
EXPORT_SYMBOL(wxvf_shutdown);
int wxvf_resume(struct device *dev_d)
{
struct pci_dev *pdev = to_pci_dev(dev_d);
struct wx *wx = pci_get_drvdata(pdev);
pci_set_master(pdev);
wx_init_interrupt_scheme(wx);
netif_device_attach(wx->netdev);
return 0;
}
EXPORT_SYMBOL(wxvf_resume);
void wxvf_remove(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
netdev = wx->netdev;
unregister_netdev(netdev);
timer_shutdown_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
kfree(wx->vfinfo);
kfree(wx->rss_key);
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
pci_disable_device(pdev);
}
EXPORT_SYMBOL(wxvf_remove);
static irqreturn_t wx_msix_misc_vf(int __always_unused irq, void *data)
{
struct wx *wx = data;
set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags);
/* Clear the interrupt */
if (!test_bit(WX_STATE_DOWN, wx->state))
wr32(wx, WX_VXIMC, wx->eims_other);
return IRQ_HANDLED;
}
int wx_request_msix_irqs_vf(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
int vector, err;
for (vector = 0; vector < wx->num_q_vectors; vector++) {
struct wx_q_vector *q_vector = wx->q_vector[vector];
struct msix_entry *entry = &wx->msix_q_entries[vector];
if (q_vector->tx.ring && q_vector->rx.ring)
snprintf(q_vector->name, sizeof(q_vector->name) - 1,
"%s-TxRx-%d", netdev->name, entry->entry);
else
/* skip this unused q_vector */
continue;
err = request_irq(entry->vector, wx_msix_clean_rings, 0,
q_vector->name, q_vector);
if (err) {
wx_err(wx, "request_irq failed for MSIX interrupt %s Error: %d\n",
q_vector->name, err);
goto free_queue_irqs;
}
}
err = request_irq(wx->msix_entry->vector, wx_msix_misc_vf,
0, netdev->name, wx);
if (err) {
wx_err(wx, "request_irq for msix_other failed: %d\n", err);
goto free_queue_irqs;
}
return 0;
free_queue_irqs:
while (vector) {
vector--;
free_irq(wx->msix_q_entries[vector].vector,
wx->q_vector[vector]);
}
wx_reset_interrupt_capability(wx);
return err;
}
EXPORT_SYMBOL(wx_request_msix_irqs_vf);
void wx_negotiate_api_vf(struct wx *wx)
{
int api[] = {
wx_mbox_api_13,
wx_mbox_api_null};
int err = 0, idx = 0;
spin_lock_bh(&wx->mbx.mbx_lock);
while (api[idx] != wx_mbox_api_null) {
err = wx_negotiate_api_version(wx, api[idx]);
if (!err)
break;
idx++;
}
spin_unlock_bh(&wx->mbx.mbx_lock);
}
EXPORT_SYMBOL(wx_negotiate_api_vf);
void wx_reset_vf(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
int ret = 0;
ret = wx_reset_hw_vf(wx);
if (!ret)
wx_init_hw_vf(wx);
wx_negotiate_api_vf(wx);
if (is_valid_ether_addr(wx->mac.addr)) {
eth_hw_addr_set(netdev, wx->mac.addr);
ether_addr_copy(netdev->perm_addr, wx->mac.addr);
}
}
EXPORT_SYMBOL(wx_reset_vf);
void wx_set_rx_mode_vf(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
unsigned int flags = netdev->flags;
int xcast_mode;
xcast_mode = (flags & IFF_ALLMULTI) ? WXVF_XCAST_MODE_ALLMULTI :
(flags & (IFF_BROADCAST | IFF_MULTICAST)) ?
WXVF_XCAST_MODE_MULTI : WXVF_XCAST_MODE_NONE;
/* request the most inclusive mode we need */
if (flags & IFF_PROMISC)
xcast_mode = WXVF_XCAST_MODE_PROMISC;
else if (flags & IFF_ALLMULTI)
xcast_mode = WXVF_XCAST_MODE_ALLMULTI;
else if (flags & (IFF_BROADCAST | IFF_MULTICAST))
xcast_mode = WXVF_XCAST_MODE_MULTI;
else
xcast_mode = WXVF_XCAST_MODE_NONE;
spin_lock_bh(&wx->mbx.mbx_lock);
wx_update_xcast_mode_vf(wx, xcast_mode);
wx_update_mc_addr_list_vf(wx, netdev);
wx_write_uc_addr_list_vf(netdev);
spin_unlock_bh(&wx->mbx.mbx_lock);
}
EXPORT_SYMBOL(wx_set_rx_mode_vf);
/**
* wx_configure_rx_vf - Configure Receive Unit after Reset
* @wx: board private structure
*
* Configure the Rx unit of the MAC after a reset.
**/
static void wx_configure_rx_vf(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
int i, ret;
wx_setup_psrtype_vf(wx);
wx_setup_vfmrqc_vf(wx);
spin_lock_bh(&wx->mbx.mbx_lock);
ret = wx_rlpml_set_vf(wx,
netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN);
spin_unlock_bh(&wx->mbx.mbx_lock);
if (ret)
wx_dbg(wx, "Failed to set MTU at %d\n", netdev->mtu);
/* Setup the HW Rx Head and Tail Descriptor Pointers and
* the Base and Length of the Rx Descriptor Ring
*/
for (i = 0; i < wx->num_rx_queues; i++) {
struct wx_ring *rx_ring = wx->rx_ring[i];
#ifdef HAVE_SWIOTLB_SKIP_CPU_SYNC
wx_set_rx_buffer_len_vf(wx, rx_ring);
#endif
wx_configure_rx_ring_vf(wx, rx_ring);
}
}
void wx_configure_vf(struct wx *wx)
{
wx_set_rx_mode_vf(wx->netdev);
wx_configure_tx_vf(wx);
wx_configure_rx_vf(wx);
}
EXPORT_SYMBOL(wx_configure_vf);
int wx_set_mac_vf(struct net_device *netdev, void *p)
{
struct wx *wx = netdev_priv(netdev);
struct sockaddr *addr = p;
int ret;
ret = eth_prepare_mac_addr_change(netdev, addr);
if (ret)
return ret;
spin_lock_bh(&wx->mbx.mbx_lock);
ret = wx_set_rar_vf(wx, 1, (u8 *)addr->sa_data, 1);
spin_unlock_bh(&wx->mbx.mbx_lock);
if (ret)
return -EPERM;
memcpy(wx->mac.addr, addr->sa_data, netdev->addr_len);
memcpy(wx->mac.perm_addr, addr->sa_data, netdev->addr_len);
eth_hw_addr_set(netdev, addr->sa_data);
return 0;
}
EXPORT_SYMBOL(wx_set_mac_vf);
void wxvf_watchdog_update_link(struct wx *wx)
{
int err;
if (!test_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags))
return;
spin_lock_bh(&wx->mbx.mbx_lock);
err = wx_check_mac_link_vf(wx);
spin_unlock_bh(&wx->mbx.mbx_lock);
if (err) {
wx->link = false;
set_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
}
clear_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags);
}
EXPORT_SYMBOL(wxvf_watchdog_update_link);
static void wxvf_irq_enable(struct wx *wx)
{
wr32(wx, WX_VXIMC, wx->eims_enable_mask);
}
static void wxvf_up_complete(struct wx *wx)
{
/* Always set the carrier off */
netif_carrier_off(wx->netdev);
mod_timer(&wx->service_timer, jiffies + HZ);
set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags);
wx_configure_msix_vf(wx);
smp_mb__before_atomic();
clear_bit(WX_STATE_DOWN, wx->state);
wx_napi_enable_all(wx);
/* clear any pending interrupts, may auto mask */
wr32(wx, WX_VXICR, U32_MAX);
wxvf_irq_enable(wx);
/* enable transmits */
netif_tx_start_all_queues(wx->netdev);
}
int wxvf_open(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
int err;
err = wx_setup_resources(wx);
if (err)
goto err_reset;
wx_configure_vf(wx);
err = wx_request_msix_irqs_vf(wx);
if (err)
goto err_free_resources;
/* Notify the stack of the actual queue counts. */
err = netif_set_real_num_tx_queues(netdev, wx->num_tx_queues);
if (err)
goto err_free_irq;
err = netif_set_real_num_rx_queues(netdev, wx->num_rx_queues);
if (err)
goto err_free_irq;
wxvf_up_complete(wx);
return 0;
err_free_irq:
wx_free_irq(wx);
err_free_resources:
wx_free_resources(wx);
err_reset:
wx_reset_vf(wx);
return err;
}
EXPORT_SYMBOL(wxvf_open);
static void wxvf_down(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
if (test_and_set_bit(WX_STATE_DOWN, wx->state))
return;
timer_delete_sync(&wx->service_timer);
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
netif_carrier_off(netdev);
wx_napi_disable_all(wx);
wx_reset_vf(wx);
wx_clean_all_tx_rings(wx);
wx_clean_all_rx_rings(wx);
}
static void wxvf_reinit_locked(struct wx *wx)
{
mutex_lock(&wx->reset_lock);
set_bit(WX_STATE_RESETTING, wx->state);
wxvf_down(wx);
wx_free_irq(wx);
wx_configure_vf(wx);
wx_request_msix_irqs_vf(wx);
wxvf_up_complete(wx);
clear_bit(WX_STATE_RESETTING, wx->state);
mutex_unlock(&wx->reset_lock);
}
static void wxvf_reset_subtask(struct wx *wx)
{
if (!test_bit(WX_FLAG_NEED_DO_RESET, wx->flags))
return;
clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
rtnl_lock();
if (test_bit(WX_STATE_RESETTING, wx->state) ||
test_bit(WX_STATE_DOWN, wx->state)) {
rtnl_unlock();
return;
}
wxvf_reinit_locked(wx);
rtnl_unlock();
}
int wxvf_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
wxvf_down(wx);
wx_free_irq(wx);
wx_free_resources(wx);
return 0;
}
EXPORT_SYMBOL(wxvf_close);
static void wxvf_link_config_subtask(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
wxvf_watchdog_update_link(wx);
if (wx->link) {
if (netif_carrier_ok(netdev))
return;
netif_carrier_on(netdev);
netdev_info(netdev, "Link is Up - %s\n",
phy_speed_to_str(wx->speed));
} else {
if (!netif_carrier_ok(netdev))
return;
netif_carrier_off(netdev);
netdev_info(netdev, "Link is Down\n");
}
}
static void wxvf_service_task(struct work_struct *work)
{
struct wx *wx = container_of(work, struct wx, service_task);
wxvf_link_config_subtask(wx);
wxvf_reset_subtask(wx);
wx_service_event_complete(wx);
}
void wxvf_init_service(struct wx *wx)
{
timer_setup(&wx->service_timer, wx_service_timer, 0);
INIT_WORK(&wx->service_task, wxvf_service_task);
clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
}
EXPORT_SYMBOL(wxvf_init_service);
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_VF_COMMON_H_
#define _WX_VF_COMMON_H_
int wxvf_suspend(struct device *dev_d);
void wxvf_shutdown(struct pci_dev *pdev);
int wxvf_resume(struct device *dev_d);
void wxvf_remove(struct pci_dev *pdev);
int wx_request_msix_irqs_vf(struct wx *wx);
void wx_negotiate_api_vf(struct wx *wx);
void wx_reset_vf(struct wx *wx);
void wx_set_rx_mode_vf(struct net_device *netdev);
void wx_configure_vf(struct wx *wx);
int wx_set_mac_vf(struct net_device *netdev, void *p);
void wxvf_watchdog_update_link(struct wx *wx);
int wxvf_open(struct net_device *netdev);
int wxvf_close(struct net_device *netdev);
void wxvf_init_service(struct wx *wx);
#endif /* _WX_VF_COMMON_H_ */
@@ -0,0 +1,292 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#include <linux/etherdevice.h>
#include <linux/pci.h>
#include "wx_type.h"
#include "wx_hw.h"
#include "wx_lib.h"
#include "wx_vf.h"
#include "wx_vf_lib.h"
void wx_write_eitr_vf(struct wx_q_vector *q_vector)
{
struct wx *wx = q_vector->wx;
int v_idx = q_vector->v_idx;
u32 itr_reg;
itr_reg = q_vector->itr & WX_VXITR_MASK;
/* set the WDIS bit to not clear the timer bits and cause an
* immediate assertion of the interrupt
*/
itr_reg |= WX_VXITR_CNT_WDIS;
wr32(wx, WX_VXITR(v_idx), itr_reg);
}
static void wx_set_ivar_vf(struct wx *wx, s8 direction, u8 queue,
u8 msix_vector)
{
u32 ivar, index;
if (direction == -1) {
/* other causes */
msix_vector |= WX_PX_IVAR_ALLOC_VAL;
ivar = rd32(wx, WX_VXIVAR_MISC);
ivar &= ~0xFF;
ivar |= msix_vector;
wr32(wx, WX_VXIVAR_MISC, ivar);
} else {
/* tx or rx causes */
msix_vector |= WX_PX_IVAR_ALLOC_VAL;
index = ((16 * (queue & 1)) + (8 * direction));
ivar = rd32(wx, WX_VXIVAR(queue >> 1));
ivar &= ~(0xFF << index);
ivar |= (msix_vector << index);
wr32(wx, WX_VXIVAR(queue >> 1), ivar);
}
}
void wx_configure_msix_vf(struct wx *wx)
{
int v_idx;
wx->eims_enable_mask = 0;
for (v_idx = 0; v_idx < wx->num_q_vectors; v_idx++) {
struct wx_q_vector *q_vector = wx->q_vector[v_idx];
struct wx_ring *ring;
wx_for_each_ring(ring, q_vector->rx)
wx_set_ivar_vf(wx, 0, ring->reg_idx, v_idx);
wx_for_each_ring(ring, q_vector->tx)
wx_set_ivar_vf(wx, 1, ring->reg_idx, v_idx);
/* add q_vector eims value to global eims_enable_mask */
wx->eims_enable_mask |= BIT(v_idx);
wx_write_eitr_vf(q_vector);
}
wx_set_ivar_vf(wx, -1, 1, v_idx);
/* setup eims_other and add value to global eims_enable_mask */
wx->eims_other = BIT(v_idx);
wx->eims_enable_mask |= wx->eims_other;
}
int wx_write_uc_addr_list_vf(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
int count = 0;
if (!netdev_uc_empty(netdev)) {
struct netdev_hw_addr *ha;
netdev_for_each_uc_addr(ha, netdev)
wx_set_uc_addr_vf(wx, ++count, ha->addr);
} else {
/*
* If the list is empty then send message to PF driver to
* clear all macvlans on this VF.
*/
wx_set_uc_addr_vf(wx, 0, NULL);
}
return count;
}
/**
* wx_configure_tx_ring_vf - Configure Tx ring after Reset
* @wx: board private structure
* @ring: structure containing ring specific data
*
* Configure the Tx descriptor ring after a reset.
**/
static void wx_configure_tx_ring_vf(struct wx *wx, struct wx_ring *ring)
{
u8 reg_idx = ring->reg_idx;
u64 tdba = ring->dma;
u32 txdctl = 0;
int ret;
/* disable queue to avoid issues while updating state */
wr32(wx, WX_VXTXDCTL(reg_idx), WX_VXTXDCTL_FLUSH);
wr32(wx, WX_VXTDBAL(reg_idx), tdba & DMA_BIT_MASK(32));
wr32(wx, WX_VXTDBAH(reg_idx), tdba >> 32);
/* enable relaxed ordering */
pcie_capability_clear_and_set_word(wx->pdev, PCI_EXP_DEVCTL,
0, PCI_EXP_DEVCTL_RELAX_EN);
/* reset head and tail pointers */
wr32(wx, WX_VXTDH(reg_idx), 0);
wr32(wx, WX_VXTDT(reg_idx), 0);
ring->tail = wx->hw_addr + WX_VXTDT(reg_idx);
/* reset ntu and ntc to place SW in sync with hardwdare */
ring->next_to_clean = 0;
ring->next_to_use = 0;
txdctl |= WX_VXTXDCTL_BUFLEN(wx_buf_len(ring->count));
txdctl |= WX_VXTXDCTL_ENABLE;
if (ring->headwb_mem) {
wr32(wx, WX_VXTXD_HEAD_ADDRL(reg_idx),
ring->headwb_dma & DMA_BIT_MASK(32));
wr32(wx, WX_VXTXD_HEAD_ADDRH(reg_idx),
upper_32_bits(ring->headwb_dma));
txdctl |= WX_VXTXDCTL_HEAD_WB;
}
/* reinitialize tx_buffer_info */
memset(ring->tx_buffer_info, 0,
sizeof(struct wx_tx_buffer) * ring->count);
wr32(wx, WX_VXTXDCTL(reg_idx), txdctl);
/* poll to verify queue is enabled */
ret = read_poll_timeout(rd32, txdctl, txdctl & WX_VXTXDCTL_ENABLE,
1000, 10000, true, wx, WX_VXTXDCTL(reg_idx));
if (ret == -ETIMEDOUT)
wx_err(wx, "Could not enable Tx Queue %d\n", reg_idx);
}
/**
* wx_configure_tx_vf - Configure Transmit Unit after Reset
* @wx: board private structure
*
* Configure the Tx unit of the MAC after a reset.
**/
void wx_configure_tx_vf(struct wx *wx)
{
u32 i;
/* Setup the HW Tx Head and Tail descriptor pointers */
for (i = 0; i < wx->num_tx_queues; i++)
wx_configure_tx_ring_vf(wx, wx->tx_ring[i]);
}
static void wx_configure_srrctl_vf(struct wx *wx, struct wx_ring *ring,
int index)
{
u32 srrctl;
srrctl = rd32m(wx, WX_VXRXDCTL(index),
(u32)~(WX_VXRXDCTL_HDRSZ_MASK | WX_VXRXDCTL_BUFSZ_MASK));
srrctl |= WX_VXRXDCTL_DROP;
srrctl |= WX_VXRXDCTL_HDRSZ(wx_hdr_sz(WX_RX_HDR_SIZE));
srrctl |= WX_VXRXDCTL_BUFSZ(wx_buf_sz(WX_RX_BUF_SIZE));
wr32(wx, WX_VXRXDCTL(index), srrctl);
}
void wx_setup_psrtype_vf(struct wx *wx)
{
/* PSRTYPE must be initialized */
u32 psrtype = WX_VXMRQC_PSR_L2HDR |
WX_VXMRQC_PSR_L3HDR |
WX_VXMRQC_PSR_L4HDR |
WX_VXMRQC_PSR_TUNHDR |
WX_VXMRQC_PSR_TUNMAC;
wr32m(wx, WX_VXMRQC, WX_VXMRQC_PSR_MASK, WX_VXMRQC_PSR(psrtype));
}
void wx_setup_vfmrqc_vf(struct wx *wx)
{
u16 rss_i = wx->num_rx_queues;
u32 vfmrqc = 0, vfreta = 0;
u8 i, j;
/* Fill out hash function seeds */
netdev_rss_key_fill(wx->rss_key, WX_RSS_KEY_SIZE);
for (i = 0; i < WX_RSS_KEY_SIZE / 4; i++)
wr32(wx, WX_VXRSSRK(i), wx->rss_key[i]);
for (i = 0, j = 0; i < WX_MAX_RETA_ENTRIES; i++, j++) {
if (j == rss_i)
j = 0;
wx->rss_indir_tbl[i] = j;
vfreta |= j << (i & 0x3) * 8;
if ((i & 3) == 3) {
wr32(wx, WX_VXRETA(i >> 2), vfreta);
vfreta = 0;
}
}
/* Perform hash on these packet types */
vfmrqc |= WX_VXMRQC_RSS_ALG_IPV4 |
WX_VXMRQC_RSS_ALG_IPV4_TCP |
WX_VXMRQC_RSS_ALG_IPV6 |
WX_VXMRQC_RSS_ALG_IPV6_TCP;
vfmrqc |= WX_VXMRQC_RSS_EN;
if (wx->num_rx_queues > 3)
vfmrqc |= WX_VXMRQC_RSS_HASH(2);
else if (wx->num_rx_queues > 1)
vfmrqc |= WX_VXMRQC_RSS_HASH(1);
wr32m(wx, WX_VXMRQC, WX_VXMRQC_RSS_MASK, WX_VXMRQC_RSS(vfmrqc));
}
void wx_configure_rx_ring_vf(struct wx *wx, struct wx_ring *ring)
{
u8 reg_idx = ring->reg_idx;
union wx_rx_desc *rx_desc;
u64 rdba = ring->dma;
u32 rxdctl;
/* disable queue to avoid issues while updating state */
rxdctl = rd32(wx, WX_VXRXDCTL(reg_idx));
wx_disable_rx_queue(wx, ring);
wr32(wx, WX_VXRDBAL(reg_idx), rdba & DMA_BIT_MASK(32));
wr32(wx, WX_VXRDBAH(reg_idx), rdba >> 32);
/* enable relaxed ordering */
pcie_capability_clear_and_set_word(wx->pdev, PCI_EXP_DEVCTL,
0, PCI_EXP_DEVCTL_RELAX_EN);
/* reset head and tail pointers */
wr32(wx, WX_VXRDH(reg_idx), 0);
wr32(wx, WX_VXRDT(reg_idx), 0);
ring->tail = wx->hw_addr + WX_VXRDT(reg_idx);
/* initialize rx_buffer_info */
memset(ring->rx_buffer_info, 0,
sizeof(struct wx_rx_buffer) * ring->count);
/* initialize Rx descriptor 0 */
rx_desc = WX_RX_DESC(ring, 0);
rx_desc->wb.upper.length = 0;
/* reset ntu and ntc to place SW in sync with hardwdare */
ring->next_to_clean = 0;
ring->next_to_use = 0;
ring->next_to_alloc = 0;
wx_configure_srrctl_vf(wx, ring, reg_idx);
/* allow any size packet since we can handle overflow */
rxdctl &= ~WX_VXRXDCTL_BUFLEN_MASK;
rxdctl |= WX_VXRXDCTL_BUFLEN(wx_buf_len(ring->count));
rxdctl |= WX_VXRXDCTL_ENABLE | WX_VXRXDCTL_VLAN;
/* enable RSC */
rxdctl &= ~WX_VXRXDCTL_RSCMAX_MASK;
rxdctl |= WX_VXRXDCTL_RSCMAX(0);
rxdctl |= WX_VXRXDCTL_RSCEN;
if (test_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags))
rxdctl |= WX_VXRXDCTL_DESC_MERGE;
wr32(wx, WX_VXRXDCTL(reg_idx), rxdctl);
/* pf/vf reuse */
wx_enable_rx_queue(wx, ring);
wx_alloc_rx_buffers(ring, wx_desc_unused(ring));
}
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2015 - 2025 Beijing WangXun Technology Co., Ltd. */
#ifndef _WX_VF_LIB_H_
#define _WX_VF_LIB_H_
void wx_write_eitr_vf(struct wx_q_vector *q_vector);
void wx_configure_msix_vf(struct wx *wx);
int wx_write_uc_addr_list_vf(struct net_device *netdev);
void wx_setup_psrtype_vf(struct wx *wx);
void wx_setup_vfmrqc_vf(struct wx *wx);
void wx_configure_tx_vf(struct wx *wx);
void wx_configure_rx_ring_vf(struct wx *wx, struct wx_ring *ring);
#endif /* _WX_VF_LIB_H_ */