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:
parent
1fbdad0005
commit
3cd1cfcba2
|
@ -355,6 +355,7 @@ struct dma_features {
|
|||
unsigned int frpes;
|
||||
unsigned int addr64;
|
||||
unsigned int rssen;
|
||||
unsigned int vlhash;
|
||||
};
|
||||
|
||||
/* GMAC TX FIFO is 8K, Rx FIFO is 16K */
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#define XGMAC_CORE_INIT_RX 0
|
||||
#define XGMAC_PACKET_FILTER 0x00000008
|
||||
#define XGMAC_FILTER_RA BIT(31)
|
||||
#define XGMAC_FILTER_VTFE BIT(16)
|
||||
#define XGMAC_FILTER_HPF BIT(10)
|
||||
#define XGMAC_FILTER_PCF BIT(7)
|
||||
#define XGMAC_FILTER_PM BIT(4)
|
||||
|
@ -51,6 +52,14 @@
|
|||
#define XGMAC_FILTER_PR BIT(0)
|
||||
#define XGMAC_HASH_TABLE(x) (0x00000010 + (x) * 4)
|
||||
#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_RXQEN(x) GENMASK((x) * 2 + 1, (x) * 2)
|
||||
#define XGMAC_RXQEN_SHIFT(x) ((x) * 2)
|
||||
|
@ -87,6 +96,7 @@
|
|||
#define XGMAC_HWFEAT_MMCSEL BIT(8)
|
||||
#define XGMAC_HWFEAT_MGKSEL BIT(7)
|
||||
#define XGMAC_HWFEAT_RWKSEL BIT(6)
|
||||
#define XGMAC_HWFEAT_VLHASH BIT(4)
|
||||
#define XGMAC_HWFEAT_GMIISEL BIT(1)
|
||||
#define XGMAC_HW_FEATURE1 0x00000120
|
||||
#define XGMAC_HWFEAT_RSSEN BIT(20)
|
||||
|
|
|
@ -490,6 +490,46 @@ static int dwxgmac2_rss_configure(struct mac_device_info *hw,
|
|||
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 = {
|
||||
.core_init = dwxgmac2_core_init,
|
||||
.set_mac = dwxgmac2_set_mac,
|
||||
|
@ -521,6 +561,7 @@ const struct stmmac_ops dwxgmac210_ops = {
|
|||
.set_filter = dwxgmac2_set_filter,
|
||||
.set_mac_loopback = dwxgmac2_set_mac_loopback,
|
||||
.rss_configure = dwxgmac2_rss_configure,
|
||||
.update_vlan_hash = dwxgmac2_update_vlan_hash,
|
||||
};
|
||||
|
||||
int dwxgmac2_setup(struct stmmac_priv *priv)
|
||||
|
|
|
@ -359,6 +359,7 @@ static void dwxgmac2_get_hw_feature(void __iomem *ioaddr,
|
|||
dma_cap->rmon = (hw_cap & XGMAC_HWFEAT_MMCSEL) >> 8;
|
||||
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->vlhash = (hw_cap & XGMAC_HWFEAT_VLHASH) >> 4;
|
||||
dma_cap->mbps_1000 = (hw_cap & XGMAC_HWFEAT_GMIISEL) >> 1;
|
||||
|
||||
/* MAC HW feature 1 */
|
||||
|
|
|
@ -336,6 +336,9 @@ struct stmmac_ops {
|
|||
/* RSS */
|
||||
int (*rss_configure)(struct mac_device_info *hw,
|
||||
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...) \
|
||||
|
@ -408,6 +411,8 @@ struct stmmac_ops {
|
|||
stmmac_do_void_callback(__priv, mac, set_mac_loopback, __args)
|
||||
#define stmmac_rss_configure(__priv, __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 */
|
||||
struct stmmac_hwtimestamp {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#define DRV_MODULE_VERSION "Jan_2016"
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/stmmac.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/pci.h>
|
||||
|
@ -191,6 +192,7 @@ struct stmmac_priv {
|
|||
spinlock_t ptp_lock;
|
||||
void __iomem *mmcaddr;
|
||||
void __iomem *ptpaddr;
|
||||
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dbgfs_dir;
|
||||
|
|
|
@ -4037,6 +4037,79 @@ static void stmmac_exit_fs(struct net_device *dev)
|
|||
}
|
||||
#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 = {
|
||||
.ndo_open = stmmac_open,
|
||||
.ndo_start_xmit = stmmac_xmit,
|
||||
|
@ -4053,6 +4126,8 @@ static const struct net_device_ops stmmac_netdev_ops = {
|
|||
.ndo_poll_controller = stmmac_poll_controller,
|
||||
#endif
|
||||
.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)
|
||||
|
@ -4307,6 +4382,10 @@ int stmmac_dvr_probe(struct device *device,
|
|||
#ifdef STMMAC_VLAN_TAG_USED
|
||||
/* Both mac100 and gmac support receive VLAN tag detection */
|
||||
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
|
||||
priv->msg_enable = netif_msg_init(debug, default_msg_level);
|
||||
|
||||
|
|
Loading…
Reference in New Issue