net: stmmac: Implement VLAN Hash Filtering in XGMAC

Implement the VLAN Hash Filtering feature in XGMAC core.

Signed-off-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Jose Abreu 2019-08-07 10:03:14 +02:00 committed by David S. Miller
parent 1fbdad0005
commit 3cd1cfcba2
7 changed files with 139 additions and 0 deletions

View File

@ -355,6 +355,7 @@ struct dma_features {
unsigned int frpes; unsigned int frpes;
unsigned int addr64; unsigned int addr64;
unsigned int rssen; unsigned int rssen;
unsigned int vlhash;
}; };
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */ /* GMAC TX FIFO is 8K, Rx FIFO is 16K */

View File

@ -44,6 +44,7 @@
#define XGMAC_CORE_INIT_RX 0 #define XGMAC_CORE_INIT_RX 0
#define XGMAC_PACKET_FILTER 0x00000008 #define XGMAC_PACKET_FILTER 0x00000008
#define XGMAC_FILTER_RA BIT(31) #define XGMAC_FILTER_RA BIT(31)
#define XGMAC_FILTER_VTFE BIT(16)
#define XGMAC_FILTER_HPF BIT(10) #define XGMAC_FILTER_HPF BIT(10)
#define XGMAC_FILTER_PCF BIT(7) #define XGMAC_FILTER_PCF BIT(7)
#define XGMAC_FILTER_PM BIT(4) #define XGMAC_FILTER_PM BIT(4)
@ -51,6 +52,14 @@
#define XGMAC_FILTER_PR BIT(0) #define XGMAC_FILTER_PR BIT(0)
#define XGMAC_HASH_TABLE(x) (0x00000010 + (x) * 4) #define XGMAC_HASH_TABLE(x) (0x00000010 + (x) * 4)
#define XGMAC_MAX_HASH_TABLE 8 #define XGMAC_MAX_HASH_TABLE 8
#define XGMAC_VLAN_TAG 0x00000050
#define XGMAC_VLAN_EDVLP BIT(26)
#define XGMAC_VLAN_VTHM BIT(25)
#define XGMAC_VLAN_DOVLTC BIT(20)
#define XGMAC_VLAN_ESVL BIT(18)
#define XGMAC_VLAN_ETV BIT(16)
#define XGMAC_VLAN_VID GENMASK(15, 0)
#define XGMAC_VLAN_HASH_TABLE 0x00000058
#define XGMAC_RXQ_CTRL0 0x000000a0 #define XGMAC_RXQ_CTRL0 0x000000a0
#define XGMAC_RXQEN(x) GENMASK((x) * 2 + 1, (x) * 2) #define XGMAC_RXQEN(x) GENMASK((x) * 2 + 1, (x) * 2)
#define XGMAC_RXQEN_SHIFT(x) ((x) * 2) #define XGMAC_RXQEN_SHIFT(x) ((x) * 2)
@ -87,6 +96,7 @@
#define XGMAC_HWFEAT_MMCSEL BIT(8) #define XGMAC_HWFEAT_MMCSEL BIT(8)
#define XGMAC_HWFEAT_MGKSEL BIT(7) #define XGMAC_HWFEAT_MGKSEL BIT(7)
#define XGMAC_HWFEAT_RWKSEL BIT(6) #define XGMAC_HWFEAT_RWKSEL BIT(6)
#define XGMAC_HWFEAT_VLHASH BIT(4)
#define XGMAC_HWFEAT_GMIISEL BIT(1) #define XGMAC_HWFEAT_GMIISEL BIT(1)
#define XGMAC_HW_FEATURE1 0x00000120 #define XGMAC_HW_FEATURE1 0x00000120
#define XGMAC_HWFEAT_RSSEN BIT(20) #define XGMAC_HWFEAT_RSSEN BIT(20)

View File

@ -490,6 +490,46 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw,
return 0; return 0;
} }
static void dwxgmac2_update_vlan_hash(struct mac_device_info *hw, u32 hash,
bool is_double)
{
void __iomem *ioaddr = hw->pcsr;
writel(hash, ioaddr + XGMAC_VLAN_HASH_TABLE);
if (hash) {
u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
value |= XGMAC_FILTER_VTFE;
writel(value, ioaddr + XGMAC_PACKET_FILTER);
value |= XGMAC_VLAN_VTHM | XGMAC_VLAN_ETV;
if (is_double) {
value |= XGMAC_VLAN_EDVLP;
value |= XGMAC_VLAN_ESVL;
value |= XGMAC_VLAN_DOVLTC;
}
writel(value, ioaddr + XGMAC_VLAN_TAG);
} else {
u32 value = readl(ioaddr + XGMAC_PACKET_FILTER);
value &= ~XGMAC_FILTER_VTFE;
writel(value, ioaddr + XGMAC_PACKET_FILTER);
value = readl(ioaddr + XGMAC_VLAN_TAG);
value &= ~(XGMAC_VLAN_VTHM | XGMAC_VLAN_ETV);
value &= ~(XGMAC_VLAN_EDVLP | XGMAC_VLAN_ESVL);
value &= ~XGMAC_VLAN_DOVLTC;
value &= ~XGMAC_VLAN_VID;
writel(value, ioaddr + XGMAC_VLAN_TAG);
}
}
const struct stmmac_ops dwxgmac210_ops = { const struct stmmac_ops dwxgmac210_ops = {
.core_init = dwxgmac2_core_init, .core_init = dwxgmac2_core_init,
.set_mac = dwxgmac2_set_mac, .set_mac = dwxgmac2_set_mac,
@ -521,6 +561,7 @@ const struct stmmac_ops dwxgmac210_ops = {
.set_filter = dwxgmac2_set_filter, .set_filter = dwxgmac2_set_filter,
.set_mac_loopback = dwxgmac2_set_mac_loopback, .set_mac_loopback = dwxgmac2_set_mac_loopback,
.rss_configure = dwxgmac2_rss_configure, .rss_configure = dwxgmac2_rss_configure,
.update_vlan_hash = dwxgmac2_update_vlan_hash,
}; };
int dwxgmac2_setup(struct stmmac_priv *priv) int dwxgmac2_setup(struct stmmac_priv *priv)

View File

@ -359,6 +359,7 @@ static void dwxgmac2_get_hw_feature(void __iomem *ioaddr,
dma_cap->rmon = (hw_cap & XGMAC_HWFEAT_MMCSEL) >> 8; dma_cap->rmon = (hw_cap & XGMAC_HWFEAT_MMCSEL) >> 8;
dma_cap->pmt_magic_frame = (hw_cap & XGMAC_HWFEAT_MGKSEL) >> 7; dma_cap->pmt_magic_frame = (hw_cap & XGMAC_HWFEAT_MGKSEL) >> 7;
dma_cap->pmt_remote_wake_up = (hw_cap & XGMAC_HWFEAT_RWKSEL) >> 6; dma_cap->pmt_remote_wake_up = (hw_cap & XGMAC_HWFEAT_RWKSEL) >> 6;
dma_cap->vlhash = (hw_cap & XGMAC_HWFEAT_VLHASH) >> 4;
dma_cap->mbps_1000 = (hw_cap & XGMAC_HWFEAT_GMIISEL) >> 1; dma_cap->mbps_1000 = (hw_cap & XGMAC_HWFEAT_GMIISEL) >> 1;
/* MAC HW feature 1 */ /* MAC HW feature 1 */

View File

@ -336,6 +336,9 @@ struct stmmac_ops {
/* RSS */ /* RSS */
int (*rss_configure)(struct mac_device_info *hw, int (*rss_configure)(struct mac_device_info *hw,
struct stmmac_rss *cfg, u32 num_rxq); struct stmmac_rss *cfg, u32 num_rxq);
/* VLAN */
void (*update_vlan_hash)(struct mac_device_info *hw, u32 hash,
bool is_double);
}; };
#define stmmac_core_init(__priv, __args...) \ #define stmmac_core_init(__priv, __args...) \
@ -408,6 +411,8 @@ struct stmmac_ops {
stmmac_do_void_callback(__priv, mac, set_mac_loopback, __args) stmmac_do_void_callback(__priv, mac, set_mac_loopback, __args)
#define stmmac_rss_configure(__priv, __args...) \ #define stmmac_rss_configure(__priv, __args...) \
stmmac_do_callback(__priv, mac, rss_configure, __args) stmmac_do_callback(__priv, mac, rss_configure, __args)
#define stmmac_update_vlan_hash(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, update_vlan_hash, __args)
/* PTP and HW Timer helpers */ /* PTP and HW Timer helpers */
struct stmmac_hwtimestamp { struct stmmac_hwtimestamp {

View File

@ -13,6 +13,7 @@
#define DRV_MODULE_VERSION "Jan_2016" #define DRV_MODULE_VERSION "Jan_2016"
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/if_vlan.h>
#include <linux/stmmac.h> #include <linux/stmmac.h>
#include <linux/phylink.h> #include <linux/phylink.h>
#include <linux/pci.h> #include <linux/pci.h>
@ -191,6 +192,7 @@ struct stmmac_priv {
spinlock_t ptp_lock; spinlock_t ptp_lock;
void __iomem *mmcaddr; void __iomem *mmcaddr;
void __iomem *ptpaddr; void __iomem *ptpaddr;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;

View File

@ -4037,6 +4037,79 @@ static void stmmac_exit_fs(struct net_device *dev)
} }
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
static u32 stmmac_vid_crc32_le(__le16 vid_le)
{
unsigned char *data = (unsigned char *)&vid_le;
unsigned char data_byte = 0;
u32 crc = ~0x0;
u32 temp = 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 ^= 0xedb88320;
}
return crc;
}
static int stmmac_vlan_update(struct stmmac_priv *priv, bool is_double)
{
u32 crc, hash = 0;
u16 vid;
for_each_set_bit(vid, priv->active_vlans, VLAN_N_VID) {
__le16 vid_le = cpu_to_le16(vid);
crc = bitrev32(~stmmac_vid_crc32_le(vid_le)) >> 28;
hash |= (1 << crc);
}
return stmmac_update_vlan_hash(priv, priv->hw, hash, is_double);
}
static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
bool is_double = false;
int ret;
if (!priv->dma_cap.vlhash)
return -EOPNOTSUPP;
if (be16_to_cpu(proto) == ETH_P_8021AD)
is_double = true;
set_bit(vid, priv->active_vlans);
ret = stmmac_vlan_update(priv, is_double);
if (ret) {
clear_bit(vid, priv->active_vlans);
return ret;
}
return ret;
}
static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)
{
struct stmmac_priv *priv = netdev_priv(ndev);
bool is_double = false;
if (!priv->dma_cap.vlhash)
return -EOPNOTSUPP;
if (be16_to_cpu(proto) == ETH_P_8021AD)
is_double = true;
clear_bit(vid, priv->active_vlans);
return stmmac_vlan_update(priv, is_double);
}
static const struct net_device_ops stmmac_netdev_ops = { static const struct net_device_ops stmmac_netdev_ops = {
.ndo_open = stmmac_open, .ndo_open = stmmac_open,
.ndo_start_xmit = stmmac_xmit, .ndo_start_xmit = stmmac_xmit,
@ -4053,6 +4126,8 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_poll_controller = stmmac_poll_controller, .ndo_poll_controller = stmmac_poll_controller,
#endif #endif
.ndo_set_mac_address = stmmac_set_mac_address, .ndo_set_mac_address = stmmac_set_mac_address,
.ndo_vlan_rx_add_vid = stmmac_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = stmmac_vlan_rx_kill_vid,
}; };
static void stmmac_reset_subtask(struct stmmac_priv *priv) static void stmmac_reset_subtask(struct stmmac_priv *priv)
@ -4307,6 +4382,10 @@ int stmmac_dvr_probe(struct device *device,
#ifdef STMMAC_VLAN_TAG_USED #ifdef STMMAC_VLAN_TAG_USED
/* Both mac100 and gmac support receive VLAN tag detection */ /* Both mac100 and gmac support receive VLAN tag detection */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX; ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
if (priv->dma_cap.vlhash) {
ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER;
}
#endif #endif
priv->msg_enable = netif_msg_init(debug, default_msg_level); priv->msg_enable = netif_msg_init(debug, default_msg_level);