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
	
	Block a user