summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/wangxun/libwx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 18:50:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 18:50:03 +0000
commit01a69402cf9d38ff180345d55c2ee51c7e89fbc7 (patch)
treeb406c5242a088c4f59c6e4b719b783f43aca6ae9 /drivers/net/ethernet/wangxun/libwx
parentAdding upstream version 6.7.12. (diff)
downloadlinux-01a69402cf9d38ff180345d55c2ee51c7e89fbc7.tar.xz
linux-01a69402cf9d38ff180345d55c2ee51c7e89fbc7.zip
Adding upstream version 6.8.9.upstream/6.8.9
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/net/ethernet/wangxun/libwx')
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ethtool.c238
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ethtool.h27
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_hw.c275
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_hw.h1
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_lib.c157
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_lib.h3
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_type.h94
7 files changed, 760 insertions, 35 deletions
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index ddc5f6d20b..cc3bec42ed 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -8,6 +8,7 @@
#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];
@@ -75,7 +76,7 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < WX_GLOBAL_STATS_LEN; i++)
- ethtool_sprintf(&p, wx_gstrings_stats[i].stat_string);
+ ethtool_puts(&p, wx_gstrings_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);
@@ -185,3 +186,238 @@ void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
}
}
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_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 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);
+
+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)
+ wx->tx_work_limit = ec->tx_max_coalesced_frames_irq;
+
+ if (wx->mac.type == wx_mac_sp)
+ max_eitr = WX_SP_MAX_EITR;
+ else
+ max_eitr = WX_EM_MAX_EITR;
+
+ if ((ec->rx_coalesce_usecs > (max_eitr >> 2)) ||
+ (ec->tx_coalesce_usecs > (max_eitr >> 2)))
+ return -EINVAL;
+
+ 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 (wx->rx_itr_setting == 1)
+ rx_itr_param = WX_20K_ITR;
+ else
+ rx_itr_param = wx->rx_itr_setting;
+
+ 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->tx_itr_setting == 1) {
+ if (wx->mac.type == wx_mac_sp)
+ tx_itr_param = WX_12K_ITR;
+ else
+ tx_itr_param = WX_20K_ITR;
+ } else {
+ 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);
+ }
+
+ 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 (wx->mac.type == wx_mac_sp)
+ 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;
+}
+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;
+
+ wx->ring_feature[RING_F_RSS].limit = count;
+
+ return 0;
+}
+EXPORT_SYMBOL(wx_set_channels);
+
+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);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
index 16d1a09369..600c3b597d 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
@@ -13,4 +13,31 @@ void wx_get_mac_stats(struct net_device *netdev,
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_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_get_msglevel(struct net_device *netdev);
+void wx_set_msglevel(struct net_device *netdev, u32 data);
#endif /* _WX_ETHTOOL_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 533e912af0..1db754615c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -149,9 +149,9 @@ void wx_irq_disable(struct wx *wx)
int vector;
for (vector = 0; vector < wx->num_q_vectors; vector++)
- synchronize_irq(wx->msix_entries[vector].vector);
+ synchronize_irq(wx->msix_q_entries[vector].vector);
- synchronize_irq(wx->msix_entries[vector].vector);
+ synchronize_irq(wx->msix_entry->vector);
} else {
synchronize_irq(pdev->irq);
}
@@ -1158,6 +1158,81 @@ static void wx_set_rxpba(struct wx *wx)
wr32(wx, WX_TDM_PB_THRE(0), txpbthresh);
}
+#define WX_ETH_FRAMING 20
+
+/**
+ * wx_hpbthresh - calculate high water mark for flow control
+ *
+ * @wx: board private structure to calculate for
+ **/
+static int wx_hpbthresh(struct wx *wx)
+{
+ struct net_device *dev = wx->netdev;
+ int link, tc, kb, marker;
+ u32 dv_id, rx_pba;
+
+ /* Calculate max LAN frame size */
+ link = dev->mtu + ETH_HLEN + ETH_FCS_LEN + WX_ETH_FRAMING;
+ tc = link;
+
+ /* Calculate delay value for device */
+ dv_id = WX_DV(link, tc);
+
+ /* Delay value is calculated in bit times convert to KB */
+ kb = WX_BT2KB(dv_id);
+ rx_pba = rd32(wx, WX_RDB_PB_SZ(0)) >> WX_RDB_PB_SZ_SHIFT;
+
+ marker = rx_pba - kb;
+
+ /* It is possible that the packet buffer is not large enough
+ * to provide required headroom. In this case throw an error
+ * to user and a do the best we can.
+ */
+ if (marker < 0) {
+ dev_warn(&wx->pdev->dev,
+ "Packet Buffer can not provide enough headroom to support flow control. Decrease MTU or number of traffic classes\n");
+ marker = tc + 1;
+ }
+
+ return marker;
+}
+
+/**
+ * wx_lpbthresh - calculate low water mark for flow control
+ *
+ * @wx: board private structure to calculate for
+ **/
+static int wx_lpbthresh(struct wx *wx)
+{
+ struct net_device *dev = wx->netdev;
+ u32 dv_id;
+ int tc;
+
+ /* Calculate max LAN frame size */
+ tc = dev->mtu + ETH_HLEN + ETH_FCS_LEN;
+
+ /* Calculate delay value for device */
+ dv_id = WX_LOW_DV(tc);
+
+ /* Delay value is calculated in bit times convert to KB */
+ return WX_BT2KB(dv_id);
+}
+
+/**
+ * wx_pbthresh_setup - calculate and setup high low water marks
+ *
+ * @wx: board private structure to calculate for
+ **/
+static void wx_pbthresh_setup(struct wx *wx)
+{
+ wx->fc.high_water = wx_hpbthresh(wx);
+ wx->fc.low_water = wx_lpbthresh(wx);
+
+ /* Low water marks must not be larger than high water marks */
+ if (wx->fc.low_water > wx->fc.high_water)
+ wx->fc.low_water = 0;
+}
+
static void wx_configure_port(struct wx *wx)
{
u32 value, i;
@@ -1522,6 +1597,72 @@ static void wx_restore_vlan(struct wx *wx)
wx_vlan_rx_add_vid(wx->netdev, htons(ETH_P_8021Q), vid);
}
+static void wx_store_reta(struct wx *wx)
+{
+ u8 *indir_tbl = wx->rss_indir_tbl;
+ u32 reta = 0;
+ u32 i;
+
+ /* Fill out the redirection table as follows:
+ * - 8 bit wide entries containing 4 bit RSS index
+ */
+ for (i = 0; i < WX_MAX_RETA_ENTRIES; i++) {
+ reta |= indir_tbl[i] << (i & 0x3) * 8;
+ if ((i & 3) == 3) {
+ wr32(wx, WX_RDB_RSSTBL(i >> 2), reta);
+ reta = 0;
+ }
+ }
+}
+
+static void wx_setup_reta(struct wx *wx)
+{
+ u16 rss_i = wx->ring_feature[RING_F_RSS].indices;
+ u32 random_key_size = WX_RSS_KEY_SIZE / 4;
+ u32 i, j;
+
+ /* Fill out hash function seeds */
+ for (i = 0; i < random_key_size; i++)
+ wr32(wx, WX_RDB_RSSRK(i), wx->rss_key[i]);
+
+ /* Fill out redirection table */
+ memset(wx->rss_indir_tbl, 0, sizeof(wx->rss_indir_tbl));
+
+ for (i = 0, j = 0; i < WX_MAX_RETA_ENTRIES; i++, j++) {
+ if (j == rss_i)
+ j = 0;
+
+ wx->rss_indir_tbl[i] = j;
+ }
+
+ wx_store_reta(wx);
+}
+
+static void wx_setup_mrqc(struct wx *wx)
+{
+ u32 rss_field = 0;
+
+ /* Disable indicating checksum in descriptor, enables RSS hash */
+ wr32m(wx, WX_PSR_CTL, WX_PSR_CTL_PCSD, WX_PSR_CTL_PCSD);
+
+ /* Perform hash on these packet types */
+ rss_field = WX_RDB_RA_CTL_RSS_IPV4 |
+ WX_RDB_RA_CTL_RSS_IPV4_TCP |
+ WX_RDB_RA_CTL_RSS_IPV4_UDP |
+ WX_RDB_RA_CTL_RSS_IPV6 |
+ WX_RDB_RA_CTL_RSS_IPV6_TCP |
+ WX_RDB_RA_CTL_RSS_IPV6_UDP;
+
+ netdev_rss_key_fill(wx->rss_key, sizeof(wx->rss_key));
+
+ wx_setup_reta(wx);
+
+ if (wx->rss_enabled)
+ rss_field |= WX_RDB_RA_CTL_RSS_EN;
+
+ wr32(wx, WX_RDB_RA_CTL, rss_field);
+}
+
/**
* wx_configure_rx - Configure Receive Unit after Reset
* @wx: pointer to private structure
@@ -1554,6 +1695,8 @@ void wx_configure_rx(struct wx *wx)
wr32(wx, WX_PSR_CTL, psrctl);
}
+ wx_setup_mrqc(wx);
+
/* set_rx_buffer_len must be called before ring initialization */
wx_set_rx_buffer_len(wx);
@@ -1584,6 +1727,7 @@ static void wx_configure_isb(struct wx *wx)
void wx_configure(struct wx *wx)
{
wx_set_rxpba(wx);
+ wx_pbthresh_setup(wx);
wx_configure_port(wx);
wx_set_rx_mode(wx->netdev);
@@ -1750,6 +1894,28 @@ int wx_get_pcie_msix_counts(struct wx *wx, u16 *msix_count, u16 max_msix_count)
}
EXPORT_SYMBOL(wx_get_pcie_msix_counts);
+/**
+ * wx_init_rss_key - Initialize wx RSS key
+ * @wx: device handle
+ *
+ * Allocates and initializes the RSS key if it is not allocated.
+ **/
+static int wx_init_rss_key(struct wx *wx)
+{
+ u32 *rss_key;
+
+ if (!wx->rss_key) {
+ rss_key = kzalloc(WX_RSS_KEY_SIZE, GFP_KERNEL);
+ if (unlikely(!rss_key))
+ return -ENOMEM;
+
+ netdev_rss_key_fill(rss_key, WX_RSS_KEY_SIZE);
+ wx->rss_key = rss_key;
+ }
+
+ return 0;
+}
+
int wx_sw_init(struct wx *wx)
{
struct pci_dev *pdev = wx->pdev;
@@ -1777,14 +1943,23 @@ int wx_sw_init(struct wx *wx)
wx->subsystem_device_id = swab16((u16)ssid);
}
+ err = wx_init_rss_key(wx);
+ if (err < 0) {
+ wx_err(wx, "rss key allocation failed\n");
+ return err;
+ }
+
wx->mac_table = kcalloc(wx->mac.num_rar_entries,
sizeof(struct wx_mac_addr),
GFP_KERNEL);
if (!wx->mac_table) {
wx_err(wx, "mac_table allocation failed\n");
+ kfree(wx->rss_key);
return -ENOMEM;
}
+ wx->msix_in_use = false;
+
return 0;
}
EXPORT_SYMBOL(wx_sw_init);
@@ -2003,6 +2178,102 @@ int wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
}
EXPORT_SYMBOL(wx_vlan_rx_kill_vid);
+static void wx_enable_rx_drop(struct wx *wx, struct wx_ring *ring)
+{
+ u16 reg_idx = ring->reg_idx;
+ u32 srrctl;
+
+ srrctl = rd32(wx, WX_PX_RR_CFG(reg_idx));
+ srrctl |= WX_PX_RR_CFG_DROP_EN;
+
+ wr32(wx, WX_PX_RR_CFG(reg_idx), srrctl);
+}
+
+static void wx_disable_rx_drop(struct wx *wx, struct wx_ring *ring)
+{
+ u16 reg_idx = ring->reg_idx;
+ u32 srrctl;
+
+ srrctl = rd32(wx, WX_PX_RR_CFG(reg_idx));
+ srrctl &= ~WX_PX_RR_CFG_DROP_EN;
+
+ wr32(wx, WX_PX_RR_CFG(reg_idx), srrctl);
+}
+
+int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause)
+{
+ u16 pause_time = WX_DEFAULT_FCPAUSE;
+ u32 mflcn_reg, fccfg_reg, reg;
+ u32 fcrtl, fcrth;
+ int i;
+
+ /* Low water mark of zero causes XOFF floods */
+ if (tx_pause && wx->fc.high_water) {
+ if (!wx->fc.low_water || wx->fc.low_water >= wx->fc.high_water) {
+ wx_err(wx, "Invalid water mark configuration\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Disable any previous flow control settings */
+ mflcn_reg = rd32(wx, WX_MAC_RX_FLOW_CTRL);
+ mflcn_reg &= ~WX_MAC_RX_FLOW_CTRL_RFE;
+
+ fccfg_reg = rd32(wx, WX_RDB_RFCC);
+ fccfg_reg &= ~WX_RDB_RFCC_RFCE_802_3X;
+
+ if (rx_pause)
+ mflcn_reg |= WX_MAC_RX_FLOW_CTRL_RFE;
+ if (tx_pause)
+ fccfg_reg |= WX_RDB_RFCC_RFCE_802_3X;
+
+ /* Set 802.3x based flow control settings. */
+ wr32(wx, WX_MAC_RX_FLOW_CTRL, mflcn_reg);
+ wr32(wx, WX_RDB_RFCC, fccfg_reg);
+
+ /* Set up and enable Rx high/low water mark thresholds, enable XON. */
+ if (tx_pause && wx->fc.high_water) {
+ fcrtl = (wx->fc.low_water << 10) | WX_RDB_RFCL_XONE;
+ wr32(wx, WX_RDB_RFCL, fcrtl);
+ fcrth = (wx->fc.high_water << 10) | WX_RDB_RFCH_XOFFE;
+ } else {
+ wr32(wx, WX_RDB_RFCL, 0);
+ /* In order to prevent Tx hangs when the internal Tx
+ * switch is enabled we must set the high water mark
+ * to the Rx packet buffer size - 24KB. This allows
+ * the Tx switch to function even under heavy Rx
+ * workloads.
+ */
+ fcrth = rd32(wx, WX_RDB_PB_SZ(0)) - 24576;
+ }
+
+ wr32(wx, WX_RDB_RFCH, fcrth);
+
+ /* Configure pause time */
+ reg = pause_time * 0x00010001;
+ wr32(wx, WX_RDB_RFCV, reg);
+
+ /* Configure flow control refresh threshold value */
+ wr32(wx, WX_RDB_RFCRT, pause_time / 2);
+
+ /* We should set the drop enable bit if:
+ * Number of Rx queues > 1 and flow control is disabled
+ *
+ * This allows us to avoid head of line blocking for security
+ * and performance reasons.
+ */
+ if (wx->num_rx_queues > 1 && !tx_pause) {
+ for (i = 0; i < wx->num_rx_queues; i++)
+ wx_enable_rx_drop(wx, wx->rx_ring[i]);
+ } else {
+ for (i = 0; i < wx->num_rx_queues; i++)
+ wx_disable_rx_drop(wx, wx->rx_ring[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(wx_fc_enable);
+
/**
* wx_update_stats - Update the board statistics counters.
* @wx: board private structure
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.h b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
index 12c20a7c36..9e219fa717 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.h
@@ -41,6 +41,7 @@ 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_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);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 347d3cec02..08d3e4069c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -1568,8 +1568,14 @@ EXPORT_SYMBOL(wx_napi_disable_all);
**/
static void wx_set_rss_queues(struct wx *wx)
{
- wx->num_rx_queues = wx->mac.max_rx_queues;
- wx->num_tx_queues = wx->mac.max_tx_queues;
+ struct wx_ring_feature *f;
+
+ /* set mask for 16 queue limit of RSS */
+ f = &wx->ring_feature[RING_F_RSS];
+ f->indices = f->limit;
+
+ wx->num_rx_queues = f->limit;
+ wx->num_tx_queues = f->limit;
}
static void wx_set_num_queues(struct wx *wx)
@@ -1592,38 +1598,54 @@ static void wx_set_num_queues(struct wx *wx)
*/
static int wx_acquire_msix_vectors(struct wx *wx)
{
- struct irq_affinity affd = {0, };
+ struct irq_affinity affd = { .pre_vectors = 1 };
int nvecs, i;
- nvecs = min_t(int, num_online_cpus(), wx->mac.max_msix_vectors);
+ /* We start by asking for one vector per queue pair */
+ nvecs = max(wx->num_rx_queues, wx->num_tx_queues);
+ nvecs = min_t(int, nvecs, num_online_cpus());
+ nvecs = min_t(int, nvecs, wx->mac.max_msix_vectors);
- wx->msix_entries = kcalloc(nvecs,
- sizeof(struct msix_entry),
- GFP_KERNEL);
- if (!wx->msix_entries)
+ wx->msix_q_entries = kcalloc(nvecs, sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (!wx->msix_q_entries)
return -ENOMEM;
+ /* One for non-queue interrupts */
+ nvecs += 1;
+
+ if (!wx->msix_in_use) {
+ wx->msix_entry = kcalloc(1, sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (!wx->msix_entry) {
+ kfree(wx->msix_q_entries);
+ wx->msix_q_entries = NULL;
+ return -ENOMEM;
+ }
+ }
+
nvecs = pci_alloc_irq_vectors_affinity(wx->pdev, nvecs,
nvecs,
PCI_IRQ_MSIX | PCI_IRQ_AFFINITY,
&affd);
if (nvecs < 0) {
wx_err(wx, "Failed to allocate MSI-X interrupts. Err: %d\n", nvecs);
- kfree(wx->msix_entries);
- wx->msix_entries = NULL;
+ kfree(wx->msix_q_entries);
+ wx->msix_q_entries = NULL;
+ kfree(wx->msix_entry);
+ wx->msix_entry = NULL;
return nvecs;
}
+ wx->msix_entry->entry = 0;
+ wx->msix_entry->vector = pci_irq_vector(wx->pdev, 0);
+ nvecs -= 1;
for (i = 0; i < nvecs; i++) {
- wx->msix_entries[i].entry = i;
- wx->msix_entries[i].vector = pci_irq_vector(wx->pdev, i);
+ wx->msix_q_entries[i].entry = i;
+ wx->msix_q_entries[i].vector = pci_irq_vector(wx->pdev, i + 1);
}
- /* one for msix_other */
- nvecs -= 1;
wx->num_q_vectors = nvecs;
- wx->num_rx_queues = nvecs;
- wx->num_tx_queues = nvecs;
return 0;
}
@@ -1645,9 +1667,11 @@ static int wx_set_interrupt_capability(struct wx *wx)
if (ret == 0 || (ret == -ENOMEM))
return ret;
- wx->num_rx_queues = 1;
- wx->num_tx_queues = 1;
- wx->num_q_vectors = 1;
+ /* Disable RSS */
+ dev_warn(&wx->pdev->dev, "Disabling RSS support\n");
+ wx->ring_feature[RING_F_RSS].limit = 1;
+
+ wx_set_num_queues(wx);
/* minmum one for queue, one for misc*/
nvecs = 1;
@@ -1905,8 +1929,12 @@ void wx_reset_interrupt_capability(struct wx *wx)
return;
if (pdev->msix_enabled) {
- kfree(wx->msix_entries);
- wx->msix_entries = NULL;
+ kfree(wx->msix_q_entries);
+ wx->msix_q_entries = NULL;
+ if (!wx->msix_in_use) {
+ kfree(wx->msix_entry);
+ wx->msix_entry = NULL;
+ }
}
pci_free_irq_vectors(wx->pdev);
}
@@ -1978,7 +2006,7 @@ void wx_free_irq(struct wx *wx)
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_entries[vector];
+ struct msix_entry *entry = &wx->msix_q_entries[vector];
/* free only the irqs that were actually requested */
if (!q_vector->rx.ring && !q_vector->tx.ring)
@@ -1988,7 +2016,7 @@ void wx_free_irq(struct wx *wx)
}
if (wx->mac.type == wx_mac_em)
- free_irq(wx->msix_entries[vector].vector, wx);
+ free_irq(wx->msix_entry->vector, wx);
}
EXPORT_SYMBOL(wx_free_irq);
@@ -2065,6 +2093,7 @@ static void wx_set_ivar(struct wx *wx, s8 direction,
wr32(wx, WX_PX_MISC_IVAR, ivar);
} else {
/* tx or rx causes */
+ msix_vector += 1; /* offset for queue vectors */
msix_vector |= WX_PX_IVAR_ALLOC_VAL;
index = ((16 * (queue & 1)) + (8 * direction));
ivar = rd32(wx, WX_PX_IVAR(queue >> 1));
@@ -2082,7 +2111,7 @@ static void wx_set_ivar(struct wx *wx, s8 direction,
* when it needs to update EITR registers at runtime. Hardware
* specific quirks/differences are taken care of here.
*/
-static void wx_write_eitr(struct wx_q_vector *q_vector)
+void wx_write_eitr(struct wx_q_vector *q_vector)
{
struct wx *wx = q_vector->wx;
int v_idx = q_vector->v_idx;
@@ -2095,7 +2124,7 @@ static void wx_write_eitr(struct wx_q_vector *q_vector)
itr_reg |= WX_PX_ITR_CNT_WDIS;
- wr32(wx, WX_PX_ITR(v_idx), itr_reg);
+ wr32(wx, WX_PX_ITR(v_idx + 1), itr_reg);
}
/**
@@ -2141,9 +2170,9 @@ void wx_configure_vectors(struct wx *wx)
wx_write_eitr(q_vector);
}
- wx_set_ivar(wx, -1, 0, v_idx);
+ wx_set_ivar(wx, -1, 0, 0);
if (pdev->msix_enabled)
- wr32(wx, WX_PX_ITR(v_idx), 1950);
+ wr32(wx, WX_PX_ITR(0), 1950);
}
EXPORT_SYMBOL(wx_configure_vectors);
@@ -2656,11 +2685,14 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
netdev_features_t changed = netdev->features ^ features;
struct wx *wx = netdev_priv(netdev);
- if (changed & NETIF_F_RXHASH)
+ if (features & NETIF_F_RXHASH) {
wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN,
WX_RDB_RA_CTL_RSS_EN);
- else
+ wx->rss_enabled = true;
+ } else {
wr32m(wx, WX_RDB_RA_CTL, WX_RDB_RA_CTL_RSS_EN, 0);
+ wx->rss_enabled = false;
+ }
if (changed &
(NETIF_F_HW_VLAN_CTAG_RX |
@@ -2671,4 +2703,71 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
}
EXPORT_SYMBOL(wx_set_features);
+void wx_set_ring(struct wx *wx, u32 new_tx_count,
+ u32 new_rx_count, struct wx_ring *temp_ring)
+{
+ int i, err = 0;
+
+ /* Setup new Tx resources and free the old Tx resources in that order.
+ * We can then assign the new resources to the rings via a memcpy.
+ * The advantage to this approach is that we are guaranteed to still
+ * have resources even in the case of an allocation failure.
+ */
+ if (new_tx_count != wx->tx_ring_count) {
+ for (i = 0; i < wx->num_tx_queues; i++) {
+ memcpy(&temp_ring[i], wx->tx_ring[i],
+ sizeof(struct wx_ring));
+
+ temp_ring[i].count = new_tx_count;
+ err = wx_setup_tx_resources(&temp_ring[i]);
+ if (err) {
+ wx_err(wx, "setup new tx resources failed, keep using the old config\n");
+ while (i) {
+ i--;
+ wx_free_tx_resources(&temp_ring[i]);
+ }
+ return;
+ }
+ }
+
+ for (i = 0; i < wx->num_tx_queues; i++) {
+ wx_free_tx_resources(wx->tx_ring[i]);
+
+ memcpy(wx->tx_ring[i], &temp_ring[i],
+ sizeof(struct wx_ring));
+ }
+
+ wx->tx_ring_count = new_tx_count;
+ }
+
+ /* Repeat the process for the Rx rings if needed */
+ if (new_rx_count != wx->rx_ring_count) {
+ for (i = 0; i < wx->num_rx_queues; i++) {
+ memcpy(&temp_ring[i], wx->rx_ring[i],
+ sizeof(struct wx_ring));
+
+ temp_ring[i].count = new_rx_count;
+ err = wx_setup_rx_resources(&temp_ring[i]);
+ if (err) {
+ wx_err(wx, "setup new rx resources failed, keep using the old config\n");
+ while (i) {
+ i--;
+ wx_free_rx_resources(&temp_ring[i]);
+ }
+ return;
+ }
+ }
+
+ for (i = 0; i < wx->num_rx_queues; i++) {
+ wx_free_rx_resources(wx->rx_ring[i]);
+ memcpy(wx->rx_ring[i], &temp_ring[i],
+ sizeof(struct wx_ring));
+ }
+
+ wx->rx_ring_count = new_rx_count;
+ }
+}
+EXPORT_SYMBOL(wx_set_ring);
+
+MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index df1f4a5951..ec909e8767 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -21,6 +21,7 @@ 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);
@@ -29,5 +30,7 @@ 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);
+void wx_set_ring(struct wx *wx, u32 new_tx_count,
+ u32 new_rx_count, struct wx_ring *temp_ring);
#endif /* _NGBE_LIB_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 83f9bb7b3c..b4dc4f3411 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -7,6 +7,7 @@
#include <linux/bitfield.h>
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
+#include <linux/phylink.h>
#include <net/ip.h>
#define WX_NCSI_SUP 0x8000
@@ -130,6 +131,15 @@
#define WX_RDB_PFCMACDAH 0x19214
#define WX_RDB_LXOFFTXC 0x19218
#define WX_RDB_LXONTXC 0x1921C
+/* Flow Control Registers */
+#define WX_RDB_RFCV 0x19200
+#define WX_RDB_RFCL 0x19220
+#define WX_RDB_RFCL_XONE BIT(31)
+#define WX_RDB_RFCH 0x19260
+#define WX_RDB_RFCH_XOFFE BIT(31)
+#define WX_RDB_RFCRT 0x192A0
+#define WX_RDB_RFCC 0x192A4
+#define WX_RDB_RFCC_RFCE_802_3X BIT(3)
/* ring assignment */
#define WX_RDB_PL_CFG(_i) (0x19300 + ((_i) * 4))
#define WX_RDB_PL_CFG_L4HDR BIT(1)
@@ -137,8 +147,16 @@
#define WX_RDB_PL_CFG_L2HDR BIT(3)
#define WX_RDB_PL_CFG_TUN_TUNHDR BIT(4)
#define WX_RDB_PL_CFG_TUN_OUTL2HDR BIT(5)
+#define WX_RDB_RSSTBL(_i) (0x19400 + ((_i) * 4))
+#define WX_RDB_RSSRK(_i) (0x19480 + ((_i) * 4))
#define WX_RDB_RA_CTL 0x194F4
#define WX_RDB_RA_CTL_RSS_EN BIT(2) /* RSS Enable */
+#define WX_RDB_RA_CTL_RSS_IPV4_TCP BIT(16)
+#define WX_RDB_RA_CTL_RSS_IPV4 BIT(17)
+#define WX_RDB_RA_CTL_RSS_IPV6 BIT(20)
+#define WX_RDB_RA_CTL_RSS_IPV6_TCP BIT(21)
+#define WX_RDB_RA_CTL_RSS_IPV4_UDP BIT(22)
+#define WX_RDB_RA_CTL_RSS_IPV6_UDP BIT(23)
/******************************* PSR Registers *******************************/
/* psr control */
@@ -305,6 +323,7 @@ enum WX_MSCA_CMD_value {
#define WX_PX_IVAR_ALLOC_VAL 0x80 /* Interrupt Allocation valid */
#define WX_7K_ITR 595
#define WX_12K_ITR 336
+#define WX_20K_ITR 200
#define WX_SP_MAX_EITR 0x00000FF8U
#define WX_EM_MAX_EITR 0x00007FFCU
@@ -330,6 +349,7 @@ enum WX_MSCA_CMD_value {
#define WX_PX_MPRC(_i) (0x01020 + ((_i) * 0x40))
/* PX_RR_CFG bit definitions */
#define WX_PX_RR_CFG_VLAN BIT(31)
+#define WX_PX_RR_CFG_DROP_EN BIT(30)
#define WX_PX_RR_CFG_SPLIT_MODE BIT(26)
#define WX_PX_RR_CFG_RR_THER_SHIFT 16
#define WX_PX_RR_CFG_RR_HDR_SZ GENMASK(15, 12)
@@ -367,8 +387,46 @@ enum WX_MSCA_CMD_value {
#define WX_MAC_STATE_MODIFIED 0x2
#define WX_MAC_STATE_IN_USE 0x4
+/* BitTimes (BT) conversion */
+#define WX_BT2KB(BT) (((BT) + (8 * 1024 - 1)) / (8 * 1024))
+#define WX_B2BT(BT) ((BT) * 8)
+
+/* Calculate Delay to respond to PFC */
+#define WX_PFC_D 672
+/* Calculate Cable Delay */
+#define WX_CABLE_DC 5556 /* Delay Copper */
+/* Calculate Delay incurred from higher layer */
+#define WX_HD 6144
+
+/* Calculate Interface Delay */
+#define WX_PHY_D 12800
+#define WX_MAC_D 4096
+#define WX_XAUI_D (2 * 1024)
+#define WX_ID (WX_MAC_D + WX_XAUI_D + WX_PHY_D)
+/* Calculate PCI Bus delay for low thresholds */
+#define WX_PCI_DELAY 10000
+
+/* Calculate delay value in bit times */
+#define WX_DV(_max_frame_link, _max_frame_tc) \
+ ((36 * (WX_B2BT(_max_frame_link) + WX_PFC_D + \
+ (2 * WX_CABLE_DC) + (2 * WX_ID) + WX_HD) / 25 + 1) + \
+ 2 * WX_B2BT(_max_frame_tc))
+
+/* Calculate low threshold delay values */
+#define WX_LOW_DV(_max_frame_tc) \
+ (2 * (2 * WX_B2BT(_max_frame_tc) + (36 * WX_PCI_DELAY / 25) + 1))
+
+/* flow control */
+#define WX_DEFAULT_FCPAUSE 0xFFFF
+
#define WX_MAX_RXD 8192
#define WX_MAX_TXD 8192
+#define WX_MIN_RXD 128
+#define WX_MIN_TXD 128
+
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define WX_REQ_RX_DESCRIPTOR_MULTIPLE 8
+#define WX_REQ_TX_DESCRIPTOR_MULTIPLE 8
#define WX_MAX_JUMBO_FRAME_SIZE 9432 /* max payload 9414 */
#define VMDQ_P(p) p
@@ -871,6 +929,19 @@ struct wx_q_vector {
struct wx_ring ring[] ____cacheline_internodealigned_in_smp;
};
+struct wx_ring_feature {
+ u16 limit; /* upper limit on feature indices */
+ u16 indices; /* current value of indices */
+ u16 mask; /* Mask used for feature to ring mapping */
+ u16 offset; /* offset to start of feature */
+};
+
+enum wx_ring_f_enum {
+ RING_F_NONE = 0,
+ RING_F_RSS,
+ RING_F_ARRAY_SIZE /* must be last in enum set */
+};
+
enum wx_isb_idx {
WX_ISB_HEADER,
WX_ISB_MISC,
@@ -879,6 +950,11 @@ enum wx_isb_idx {
WX_ISB_MAX
};
+struct wx_fc_info {
+ u32 high_water; /* Flow Ctrl High-water */
+ u32 low_water; /* Flow Ctrl Low-water */
+};
+
/* Statistics counters collected by the MAC */
struct wx_hw_stats {
u64 gprc;
@@ -919,6 +995,7 @@ struct wx {
enum sp_media_type media_type;
struct wx_eeprom_info eeprom;
struct wx_addr_filter_info addr_ctrl;
+ struct wx_fc_info fc;
struct wx_mac_addr *mac_table;
u16 device_id;
u16 vendor_id;
@@ -939,6 +1016,8 @@ struct wx {
int speed;
int duplex;
struct phy_device *phydev;
+ struct phylink *phylink;
+ struct phylink_config phylink_config;
bool wol_hw_supported;
bool ncsi_enabled;
@@ -966,7 +1045,10 @@ struct wx {
struct wx_q_vector *q_vector[64];
unsigned int queues_per_pool;
- struct msix_entry *msix_entries;
+ struct msix_entry *msix_q_entries;
+ struct msix_entry *msix_entry;
+ bool msix_in_use;
+ struct wx_ring_feature ring_feature[RING_F_ARRAY_SIZE];
/* misc interrupt status block */
dma_addr_t isb_dma;
@@ -974,8 +1056,9 @@ struct wx {
u32 isb_tag[WX_ISB_MAX];
#define WX_MAX_RETA_ENTRIES 128
+#define WX_RSS_INDIR_TBL_MAX 64
u8 rss_indir_tbl[WX_MAX_RETA_ENTRIES];
-
+ bool rss_enabled;
#define WX_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */
u32 *rss_key;
u32 wol;
@@ -992,7 +1075,7 @@ struct wx {
};
#define WX_INTR_ALL (~0ULL)
-#define WX_INTR_Q(i) BIT(i)
+#define WX_INTR_Q(i) BIT((i) + 1)
/* register operations */
#define wr32(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
@@ -1044,4 +1127,9 @@ rd64(struct wx *wx, u32 reg)
#define wx_dbg(wx, fmt, arg...) \
dev_dbg(&(wx)->pdev->dev, fmt, ##arg)
+static inline struct wx *phylink_to_wx(struct phylink_config *config)
+{
+ return container_of(config, struct wx, phylink_config);
+}
+
#endif /* _WX_TYPE_H_ */