Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 801c62d9 authored by Lendacky, Thomas's avatar Lendacky, Thomas Committed by David S. Miller
Browse files

amd-xgbe: Add support for VLAN filtering



This patch adds support for (imperfect) filtering of
VLAN tag ids using a 16-bit filter hash table.  When
VLANs are added, a 4-bit hash is calculated with the
result indicating the bit in the hash table to set.
This table is used by the hardware to drop packets with
a VLAN id that does not hash to a set bit in the table.

Signed-off-by: default avatarTom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c52e9c63
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -182,6 +182,7 @@ config AMD_XGBE
	depends on OF_NET
	select PHYLIB
	select AMD_XGBE_PHY
	select BITREVERSE
	---help---
	  This driver supports the AMD 10GbE Ethernet device found on an
	  AMD SoC.
+12 −0
Original line number Diff line number Diff line
@@ -393,6 +393,8 @@
#define MAC_PFR_PM_WIDTH		1
#define MAC_PFR_PR_INDEX		0
#define MAC_PFR_PR_WIDTH		1
#define MAC_PFR_VTFE_INDEX		16
#define MAC_PFR_VTFE_WIDTH		1
#define MAC_PMTCSR_MGKPKTEN_INDEX	1
#define MAC_PMTCSR_MGKPKTEN_WIDTH	1
#define MAC_PMTCSR_PWRDWN_INDEX		0
@@ -427,6 +429,8 @@
#define MAC_TCR_SS_WIDTH		2
#define MAC_TCR_TE_INDEX		0
#define MAC_TCR_TE_WIDTH		1
#define MAC_VLANHTR_VLHT_INDEX		0
#define MAC_VLANHTR_VLHT_WIDTH		16
#define MAC_VLANIR_VLTI_INDEX		20
#define MAC_VLANIR_VLTI_WIDTH		1
#define MAC_VLANIR_CSVL_INDEX		19
@@ -437,10 +441,18 @@
#define MAC_VLANTR_ERSVLM_WIDTH		1
#define MAC_VLANTR_ESVL_INDEX		18
#define MAC_VLANTR_ESVL_WIDTH		1
#define MAC_VLANTR_ETV_INDEX		16
#define MAC_VLANTR_ETV_WIDTH		1
#define MAC_VLANTR_EVLS_INDEX		21
#define MAC_VLANTR_EVLS_WIDTH		2
#define MAC_VLANTR_EVLRXS_INDEX		24
#define MAC_VLANTR_EVLRXS_WIDTH		1
#define MAC_VLANTR_VL_INDEX		0
#define MAC_VLANTR_VL_WIDTH		16
#define MAC_VLANTR_VTHM_INDEX		25
#define MAC_VLANTR_VTHM_WIDTH		1
#define MAC_VLANTR_VTIM_INDEX		17
#define MAC_VLANTR_VTIM_WIDTH		1
#define MAC_VR_DEVID_INDEX		8
#define MAC_VR_DEVID_WIDTH		8
#define MAC_VR_SNPSVER_INDEX		0
+95 −0
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@

#include <linux/phy.h>
#include <linux/clk.h>
#include <linux/bitrev.h>

#include "xgbe.h"
#include "xgbe-common.h"
@@ -738,6 +739,89 @@ static int xgbe_disable_rx_vlan_stripping(struct xgbe_prv_data *pdata)
	return 0;
}

static int xgbe_enable_rx_vlan_filtering(struct xgbe_prv_data *pdata)
{
	/* Enable VLAN filtering */
	XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VTFE, 1);

	/* Enable VLAN Hash Table filtering */
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VTHM, 1);

	/* Disable VLAN tag inverse matching */
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VTIM, 0);

	/* Only filter on the lower 12-bits of the VLAN tag */
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ETV, 1);

	/* In order for the VLAN Hash Table filtering to be effective,
	 * the VLAN tag identifier in the VLAN Tag Register must not
	 * be zero.  Set the VLAN tag identifier to "1" to enable the
	 * VLAN Hash Table filtering.  This implies that a VLAN tag of
	 * 1 will always pass filtering.
	 */
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VL, 1);

	return 0;
}

static int xgbe_disable_rx_vlan_filtering(struct xgbe_prv_data *pdata)
{
	/* Disable VLAN filtering */
	XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VTFE, 0);

	return 0;
}

#ifndef CRCPOLY_LE
#define CRCPOLY_LE 0xedb88320
#endif
static u32 xgbe_vid_crc32_le(__le16 vid_le)
{
	u32 poly = CRCPOLY_LE;
	u32 crc = ~0;
	u32 temp = 0;
	unsigned char *data = (unsigned char *)&vid_le;
	unsigned char data_byte = 0;
	int i, bits;

	bits = get_bitmask_order(VLAN_VID_MASK);
	for (i = 0; i < bits; i++) {
		if ((i % 8) == 0)
			data_byte = data[i / 8];

		temp = ((crc & 1) ^ data_byte) & 1;
		crc >>= 1;
		data_byte >>= 1;

		if (temp)
			crc ^= poly;
	}

	return crc;
}

static int xgbe_update_vlan_hash_table(struct xgbe_prv_data *pdata)
{
	u32 crc;
	u16 vid;
	__le16 vid_le;
	u16 vlan_hash_table = 0;

	/* Generate the VLAN Hash Table value */
	for_each_set_bit(vid, pdata->active_vlans, VLAN_N_VID) {
		/* Get the CRC32 value of the VLAN ID */
		vid_le = cpu_to_le16(vid);
		crc = bitrev32(~xgbe_vid_crc32_le(vid_le)) >> 28;

		vlan_hash_table |= (1 << crc);
	}

	/* Set the VLAN Hash Table filtering register */
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANHTR, VLHT, vlan_hash_table);

	return 0;
}

static void xgbe_tx_desc_reset(struct xgbe_ring_data *rdata)
{
	struct xgbe_ring_desc *rdesc = rdata->rdesc;
@@ -1547,6 +1631,14 @@ static void xgbe_config_vlan_support(struct xgbe_prv_data *pdata)
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANIR, CSVL, 0);
	XGMAC_IOWRITE_BITS(pdata, MAC_VLANIR, VLTI, 1);

	/* Set the current VLAN Hash Table register value */
	xgbe_update_vlan_hash_table(pdata);

	if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
		xgbe_enable_rx_vlan_filtering(pdata);
	else
		xgbe_disable_rx_vlan_filtering(pdata);

	if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
		xgbe_enable_rx_vlan_stripping(pdata);
	else
@@ -2118,6 +2210,9 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)

	hw_if->enable_rx_vlan_stripping = xgbe_enable_rx_vlan_stripping;
	hw_if->disable_rx_vlan_stripping = xgbe_disable_rx_vlan_stripping;
	hw_if->enable_rx_vlan_filtering = xgbe_enable_rx_vlan_filtering;
	hw_if->disable_rx_vlan_filtering = xgbe_disable_rx_vlan_filtering;
	hw_if->update_vlan_hash_table = xgbe_update_vlan_hash_table;

	hw_if->read_mmd_regs = xgbe_read_mmd_regs;
	hw_if->write_mmd_regs = xgbe_write_mmd_regs;
+47 −13
Original line number Diff line number Diff line
@@ -1000,6 +1000,38 @@ static struct rtnl_link_stats64 *xgbe_get_stats64(struct net_device *netdev,
	return s;
}

static int xgbe_vlan_rx_add_vid(struct net_device *netdev, __be16 proto,
				u16 vid)
{
	struct xgbe_prv_data *pdata = netdev_priv(netdev);
	struct xgbe_hw_if *hw_if = &pdata->hw_if;

	DBGPR("-->%s\n", __func__);

	set_bit(vid, pdata->active_vlans);
	hw_if->update_vlan_hash_table(pdata);

	DBGPR("<--%s\n", __func__);

	return 0;
}

static int xgbe_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto,
				 u16 vid)
{
	struct xgbe_prv_data *pdata = netdev_priv(netdev);
	struct xgbe_hw_if *hw_if = &pdata->hw_if;

	DBGPR("-->%s\n", __func__);

	clear_bit(vid, pdata->active_vlans);
	hw_if->update_vlan_hash_table(pdata);

	DBGPR("<--%s\n", __func__);

	return 0;
}

#ifdef CONFIG_NET_POLL_CONTROLLER
static void xgbe_poll_controller(struct net_device *netdev)
{
@@ -1022,26 +1054,26 @@ static int xgbe_set_features(struct net_device *netdev,
{
	struct xgbe_prv_data *pdata = netdev_priv(netdev);
	struct xgbe_hw_if *hw_if = &pdata->hw_if;
	unsigned int rxcsum_enabled, rxvlan_enabled;
	unsigned int rxcsum, rxvlan, rxvlan_filter;

	rxcsum_enabled = !!(pdata->netdev_features & NETIF_F_RXCSUM);
	rxvlan_enabled = !!(pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX);
	rxcsum = pdata->netdev_features & NETIF_F_RXCSUM;
	rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX;
	rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER;

	if ((features & NETIF_F_RXCSUM) && !rxcsum_enabled) {
	if ((features & NETIF_F_RXCSUM) && !rxcsum)
		hw_if->enable_rx_csum(pdata);
		netdev_alert(netdev, "state change - rxcsum enabled\n");
	} else if (!(features & NETIF_F_RXCSUM) && rxcsum_enabled) {
	else if (!(features & NETIF_F_RXCSUM) && rxcsum)
		hw_if->disable_rx_csum(pdata);
		netdev_alert(netdev, "state change - rxcsum disabled\n");
	}

	if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan_enabled) {
	if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan)
		hw_if->enable_rx_vlan_stripping(pdata);
		netdev_alert(netdev, "state change - rxvlan enabled\n");
	} else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan_enabled) {
	else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan)
		hw_if->disable_rx_vlan_stripping(pdata);
		netdev_alert(netdev, "state change - rxvlan disabled\n");
	}

	if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter)
		hw_if->enable_rx_vlan_filtering(pdata);
	else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter)
		hw_if->disable_rx_vlan_filtering(pdata);

	pdata->netdev_features = features;

@@ -1059,6 +1091,8 @@ static const struct net_device_ops xgbe_netdev_ops = {
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_change_mtu		= xgbe_change_mtu,
	.ndo_get_stats64	= xgbe_get_stats64,
	.ndo_vlan_rx_add_vid	= xgbe_vlan_rx_add_vid,
	.ndo_vlan_rx_kill_vid	= xgbe_vlan_rx_kill_vid,
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	= xgbe_poll_controller,
#endif
+2 −1
Original line number Diff line number Diff line
@@ -385,7 +385,8 @@ static int xgbe_probe(struct platform_device *pdev)
			      NETIF_F_TSO6 |
			      NETIF_F_GRO |
			      NETIF_F_HW_VLAN_CTAG_RX |
			      NETIF_F_HW_VLAN_CTAG_TX;
			      NETIF_F_HW_VLAN_CTAG_TX |
			      NETIF_F_HW_VLAN_CTAG_FILTER;

	netdev->vlan_features |= NETIF_F_SG |
				 NETIF_F_IP_CSUM |
Loading