net: wangxun: Implement vlan add and kill functions
authorMengyuan Lou <mengyuanlou@net-swift.com>
Tue, 30 May 2023 02:26:27 +0000 (10:26 +0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 1 Jun 2023 06:02:26 +0000 (23:02 -0700)
Implement vlan add/kill functions which add and remove
vlan id in hardware.

Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/wangxun/libwx/wx_hw.c
drivers/net/ethernet/wangxun/libwx/wx_hw.h
drivers/net/ethernet/wangxun/libwx/wx_lib.c
drivers/net/ethernet/wangxun/libwx/wx_type.h

index ca409b4054d0609f97ec15da5de1c8c4aecd2bbb..39a9aeee7aab228d96c578d1411f9ab140c4487f 100644 (file)
@@ -1182,12 +1182,28 @@ static void wx_enable_sec_rx_path(struct wx *wx)
        WX_WRITE_FLUSH(wx);
 }
 
+static void wx_vlan_strip_control(struct wx *wx, bool enable)
+{
+       int i, j;
+
+       for (i = 0; i < wx->num_rx_queues; i++) {
+               struct wx_ring *ring = wx->rx_ring[i];
+
+               j = ring->reg_idx;
+               wr32m(wx, WX_PX_RR_CFG(j), WX_PX_RR_CFG_VLAN,
+                     enable ? WX_PX_RR_CFG_VLAN : 0);
+       }
+}
+
 void wx_set_rx_mode(struct net_device *netdev)
 {
        struct wx *wx = netdev_priv(netdev);
+       netdev_features_t features;
        u32 fctrl, vmolr, vlnctrl;
        int count;
 
+       features = netdev->features;
+
        /* Check for Promiscuous and All Multicast modes */
        fctrl = rd32(wx, WX_PSR_CTL);
        fctrl &= ~(WX_PSR_CTL_UPE | WX_PSR_CTL_MPE);
@@ -1254,6 +1270,13 @@ void wx_set_rx_mode(struct net_device *netdev)
        wr32(wx, WX_PSR_VLAN_CTL, vlnctrl);
        wr32(wx, WX_PSR_CTL, fctrl);
        wr32(wx, WX_PSR_VM_L2CTL(0), vmolr);
+
+       if ((features & NETIF_F_HW_VLAN_CTAG_RX) &&
+           (features & NETIF_F_HW_VLAN_STAG_RX))
+               wx_vlan_strip_control(wx, true);
+       else
+               wx_vlan_strip_control(wx, false);
+
 }
 EXPORT_SYMBOL(wx_set_rx_mode);
 
@@ -1462,6 +1485,16 @@ static void wx_configure_tx(struct wx *wx)
              WX_MAC_TX_CFG_TE, WX_MAC_TX_CFG_TE);
 }
 
+static void wx_restore_vlan(struct wx *wx)
+{
+       u16 vid = 1;
+
+       wx_vlan_rx_add_vid(wx->netdev, htons(ETH_P_8021Q), 0);
+
+       for_each_set_bit_from(vid, wx->active_vlans, VLAN_N_VID)
+               wx_vlan_rx_add_vid(wx->netdev, htons(ETH_P_8021Q), vid);
+}
+
 /**
  * wx_configure_rx - Configure Receive Unit after Reset
  * @wx: pointer to private structure
@@ -1527,7 +1560,7 @@ void wx_configure(struct wx *wx)
        wx_configure_port(wx);
 
        wx_set_rx_mode(wx->netdev);
-
+       wx_restore_vlan(wx);
        wx_enable_sec_rx_path(wx);
 
        wx_configure_tx(wx);
@@ -1727,4 +1760,241 @@ int wx_sw_init(struct wx *wx)
 }
 EXPORT_SYMBOL(wx_sw_init);
 
+/**
+ *  wx_find_vlvf_slot - find the vlanid or the first empty slot
+ *  @wx: pointer to hardware structure
+ *  @vlan: VLAN id to write to VLAN filter
+ *
+ *  return the VLVF index where this VLAN id should be placed
+ *
+ **/
+static int wx_find_vlvf_slot(struct wx *wx, u32 vlan)
+{
+       u32 bits = 0, first_empty_slot = 0;
+       int regindex;
+
+       /* short cut the special case */
+       if (vlan == 0)
+               return 0;
+
+       /* Search for the vlan id in the VLVF entries. Save off the first empty
+        * slot found along the way
+        */
+       for (regindex = 1; regindex < WX_PSR_VLAN_SWC_ENTRIES; regindex++) {
+               wr32(wx, WX_PSR_VLAN_SWC_IDX, regindex);
+               bits = rd32(wx, WX_PSR_VLAN_SWC);
+               if (!bits && !(first_empty_slot))
+                       first_empty_slot = regindex;
+               else if ((bits & 0x0FFF) == vlan)
+                       break;
+       }
+
+       if (regindex >= WX_PSR_VLAN_SWC_ENTRIES) {
+               if (first_empty_slot)
+                       regindex = first_empty_slot;
+               else
+                       regindex = -ENOMEM;
+       }
+
+       return regindex;
+}
+
+/**
+ *  wx_set_vlvf - Set VLAN Pool Filter
+ *  @wx: pointer to hardware structure
+ *  @vlan: VLAN id to write to VLAN filter
+ *  @vind: VMDq output index that maps queue to VLAN id in VFVFB
+ *  @vlan_on: boolean flag to turn on/off VLAN in VFVF
+ *  @vfta_changed: pointer to boolean flag which indicates whether VFTA
+ *                 should be changed
+ *
+ *  Turn on/off specified bit in VLVF table.
+ **/
+static int wx_set_vlvf(struct wx *wx, u32 vlan, u32 vind, bool vlan_on,
+                      bool *vfta_changed)
+{
+       int vlvf_index;
+       u32 vt, bits;
+
+       /* If VT Mode is set
+        *   Either vlan_on
+        *     make sure the vlan is in VLVF
+        *     set the vind bit in the matching VLVFB
+        *   Or !vlan_on
+        *     clear the pool bit and possibly the vind
+        */
+       vt = rd32(wx, WX_CFG_PORT_CTL);
+       if (!(vt & WX_CFG_PORT_CTL_NUM_VT_MASK))
+               return 0;
+
+       vlvf_index = wx_find_vlvf_slot(wx, vlan);
+       if (vlvf_index < 0)
+               return vlvf_index;
+
+       wr32(wx, WX_PSR_VLAN_SWC_IDX, vlvf_index);
+       if (vlan_on) {
+               /* set the pool bit */
+               if (vind < 32) {
+                       bits = rd32(wx, WX_PSR_VLAN_SWC_VM_L);
+                       bits |= (1 << vind);
+                       wr32(wx, WX_PSR_VLAN_SWC_VM_L, bits);
+               } else {
+                       bits = rd32(wx, WX_PSR_VLAN_SWC_VM_H);
+                       bits |= (1 << (vind - 32));
+                       wr32(wx, WX_PSR_VLAN_SWC_VM_H, bits);
+               }
+       } else {
+               /* clear the pool bit */
+               if (vind < 32) {
+                       bits = rd32(wx, WX_PSR_VLAN_SWC_VM_L);
+                       bits &= ~(1 << vind);
+                       wr32(wx, WX_PSR_VLAN_SWC_VM_L, bits);
+                       bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_H);
+               } else {
+                       bits = rd32(wx, WX_PSR_VLAN_SWC_VM_H);
+                       bits &= ~(1 << (vind - 32));
+                       wr32(wx, WX_PSR_VLAN_SWC_VM_H, bits);
+                       bits |= rd32(wx, WX_PSR_VLAN_SWC_VM_L);
+               }
+       }
+
+       if (bits) {
+               wr32(wx, WX_PSR_VLAN_SWC, (WX_PSR_VLAN_SWC_VIEN | vlan));
+               if (!vlan_on && vfta_changed)
+                       *vfta_changed = false;
+       } else {
+               wr32(wx, WX_PSR_VLAN_SWC, 0);
+       }
+
+       return 0;
+}
+
+/**
+ *  wx_set_vfta - Set VLAN filter table
+ *  @wx: pointer to hardware structure
+ *  @vlan: VLAN id to write to VLAN filter
+ *  @vind: VMDq output index that maps queue to VLAN id in VFVFB
+ *  @vlan_on: boolean flag to turn on/off VLAN in VFVF
+ *
+ *  Turn on/off specified VLAN in the VLAN filter table.
+ **/
+static int wx_set_vfta(struct wx *wx, u32 vlan, u32 vind, bool vlan_on)
+{
+       u32 bitindex, vfta, targetbit;
+       bool vfta_changed = false;
+       int regindex, ret;
+
+       /* this is a 2 part operation - first the VFTA, then the
+        * VLVF and VLVFB if VT Mode is set
+        * We don't write the VFTA until we know the VLVF part succeeded.
+        */
+
+       /* Part 1
+        * The VFTA is a bitstring made up of 128 32-bit registers
+        * that enable the particular VLAN id, much like the MTA:
+        *    bits[11-5]: which register
+        *    bits[4-0]:  which bit in the register
+        */
+       regindex = (vlan >> 5) & 0x7F;
+       bitindex = vlan & 0x1F;
+       targetbit = (1 << bitindex);
+       /* errata 5 */
+       vfta = wx->mac.vft_shadow[regindex];
+       if (vlan_on) {
+               if (!(vfta & targetbit)) {
+                       vfta |= targetbit;
+                       vfta_changed = true;
+               }
+       } else {
+               if ((vfta & targetbit)) {
+                       vfta &= ~targetbit;
+                       vfta_changed = true;
+               }
+       }
+       /* Part 2
+        * Call wx_set_vlvf to set VLVFB and VLVF
+        */
+       ret = wx_set_vlvf(wx, vlan, vind, vlan_on, &vfta_changed);
+       if (ret != 0)
+               return ret;
+
+       if (vfta_changed)
+               wr32(wx, WX_PSR_VLAN_TBL(regindex), vfta);
+       wx->mac.vft_shadow[regindex] = vfta;
+
+       return 0;
+}
+
+/**
+ *  wx_clear_vfta - Clear VLAN filter table
+ *  @wx: pointer to hardware structure
+ *
+ *  Clears the VLAN filer table, and the VMDq index associated with the filter
+ **/
+static void wx_clear_vfta(struct wx *wx)
+{
+       u32 offset;
+
+       for (offset = 0; offset < wx->mac.vft_size; offset++) {
+               wr32(wx, WX_PSR_VLAN_TBL(offset), 0);
+               wx->mac.vft_shadow[offset] = 0;
+       }
+
+       for (offset = 0; offset < WX_PSR_VLAN_SWC_ENTRIES; offset++) {
+               wr32(wx, WX_PSR_VLAN_SWC_IDX, offset);
+               wr32(wx, WX_PSR_VLAN_SWC, 0);
+               wr32(wx, WX_PSR_VLAN_SWC_VM_L, 0);
+               wr32(wx, WX_PSR_VLAN_SWC_VM_H, 0);
+       }
+}
+
+int wx_vlan_rx_add_vid(struct net_device *netdev,
+                      __be16 proto, u16 vid)
+{
+       struct wx *wx = netdev_priv(netdev);
+
+       /* add VID to filter table */
+       wx_set_vfta(wx, vid, VMDQ_P(0), true);
+       set_bit(vid, wx->active_vlans);
+
+       return 0;
+}
+EXPORT_SYMBOL(wx_vlan_rx_add_vid);
+
+int wx_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid)
+{
+       struct wx *wx = netdev_priv(netdev);
+
+       /* remove VID from filter table */
+       if (vid)
+               wx_set_vfta(wx, vid, VMDQ_P(0), false);
+       clear_bit(vid, wx->active_vlans);
+
+       return 0;
+}
+EXPORT_SYMBOL(wx_vlan_rx_kill_vid);
+
+/**
+ *  wx_start_hw - Prepare hardware for Tx/Rx
+ *  @wx: pointer to hardware structure
+ *
+ *  Starts the hardware using the generic start_hw function
+ *  and the generation start_hw function.
+ *  Then performs revision-specific operations, if any.
+ **/
+void wx_start_hw(struct wx *wx)
+{
+       int i;
+
+       /* Clear the VLAN filter table */
+       wx_clear_vfta(wx);
+       WX_WRITE_FLUSH(wx);
+       /* Clear the rate limiters */
+       for (i = 0; i < wx->mac.max_tx_queues; i++) {
+               wr32(wx, WX_TDM_RP_IDX, i);
+               wr32(wx, WX_TDM_RP_RATE, 0);
+       }
+}
+EXPORT_SYMBOL(wx_start_hw);
+
 MODULE_LICENSE("GPL");
index c173c56f0ab5d2f28aa14fb2db7fe1e039c714c1..1f93ca32c92125b695860b20a4414d7e81331f21 100644 (file)
@@ -26,10 +26,13 @@ void wx_set_rx_mode(struct net_device *netdev);
 int wx_change_mtu(struct net_device *netdev, int new_mtu);
 void wx_disable_rx_queue(struct wx *wx, struct wx_ring *ring);
 void wx_configure(struct wx *wx);
+void wx_start_hw(struct wx *wx);
 int wx_disable_pcie_master(struct wx *wx);
 int wx_stop_adapter(struct wx *wx);
 void wx_reset_misc(struct wx *wx);
 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);
 
 #endif /* _WX_HW_H_ */
index 84107208401e491255e6b34f944fb83dc830f1fb..680f1ad36240ee47e323ee78f2e8feef2c988a2b 100644 (file)
@@ -634,6 +634,23 @@ static void wx_rx_checksum(struct wx_ring *ring,
        ring->rx_stats.csum_good_cnt++;
 }
 
+static void wx_rx_vlan(struct wx_ring *ring, union wx_rx_desc *rx_desc,
+                      struct sk_buff *skb)
+{
+       u16 ethertype;
+       u8 idx = 0;
+
+       if ((ring->netdev->features &
+            (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX)) &&
+           wx_test_staterr(rx_desc, WX_RXD_STAT_VP)) {
+               idx = (le16_to_cpu(rx_desc->wb.lower.lo_dword.hs_rss.pkt_info) &
+                      0x1c0) >> 6;
+               ethertype = ring->q_vector->wx->tpid[idx];
+               __vlan_hwaccel_put_tag(skb, htons(ethertype),
+                                      le16_to_cpu(rx_desc->wb.upper.vlan));
+       }
+}
+
 /**
  * wx_process_skb_fields - Populate skb header fields from Rx descriptor
  * @rx_ring: rx descriptor ring packet is being transacted on
@@ -650,6 +667,7 @@ static void wx_process_skb_fields(struct wx_ring *rx_ring,
 {
        wx_rx_hash(rx_ring, rx_desc, skb);
        wx_rx_checksum(rx_ring, rx_desc, skb);
+       wx_rx_vlan(rx_ring, rx_desc, skb);
        skb_record_rx_queue(skb, rx_ring->queue_index);
        skb->protocol = eth_type_trans(skb, rx_ring->netdev);
 }
index 2c1fd0f1025d4da32b777079d55ecc157b22319b..91b2e4bfa206ab29551a929e0f9500091a4ba080 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/bitfield.h>
 #include <linux/netdevice.h>
+#include <linux/if_vlan.h>
 #include <net/ip.h>
 
 #define WX_NCSI_SUP                             0x8000
@@ -65,6 +66,8 @@
 #define WX_CFG_PORT_CTL_QINQ         BIT(2)
 #define WX_CFG_PORT_CTL_D_VLAN       BIT(0) /* double vlan*/
 #define WX_CFG_TAG_TPID(_i)          (0x14430 + ((_i) * 4))
+#define WX_CFG_PORT_CTL_NUM_VT_MASK  GENMASK(13, 12) /* number of TVs */
+
 
 /* GPIO Registers */
 #define WX_GPIO_DR                   0x14800
@@ -88,6 +91,8 @@
 /* TDM CTL BIT */
 #define WX_TDM_CTL_TE                BIT(0) /* Transmit Enable */
 #define WX_TDM_PB_THRE(_i)           (0x18020 + ((_i) * 4))
+#define WX_TDM_RP_IDX                0x1820C
+#define WX_TDM_RP_RATE               0x18404
 
 /***************************** RDB registers *********************************/
 /* receive packet buffer */
 #define WX_PSR_LAN_FLEX_DW_H(_i)     (0x15C04 + ((_i) * 16))
 #define WX_PSR_LAN_FLEX_MSK(_i)      (0x15C08 + ((_i) * 16))
 
+/* vlan tbl */
+#define WX_PSR_VLAN_TBL(_i)          (0x16000 + ((_i) * 4))
+
 /* mac switcher */
 #define WX_PSR_MAC_SWC_AD_L          0x16200
 #define WX_PSR_MAC_SWC_AD_H          0x16204
 #define WX_PSR_MAC_SWC_IDX           0x16210
 #define WX_CLEAR_VMDQ_ALL            0xFFFFFFFFU
 
+/* vlan switch */
+#define WX_PSR_VLAN_SWC              0x16220
+#define WX_PSR_VLAN_SWC_VM_L         0x16224
+#define WX_PSR_VLAN_SWC_VM_H         0x16228
+#define WX_PSR_VLAN_SWC_IDX          0x16230         /* 64 vlan entries */
+/* VLAN pool filtering masks */
+#define WX_PSR_VLAN_SWC_VIEN         BIT(31)  /* filter is valid */
+#define WX_PSR_VLAN_SWC_ENTRIES      64
+
 /********************************* RSEC **************************************/
 /* general rsec */
 #define WX_RSC_CTL                   0x17000
 #define WX_PX_RR_RP(_i)              (0x0100C + ((_i) * 0x40))
 #define WX_PX_RR_CFG(_i)             (0x01010 + ((_i) * 0x40))
 /* PX_RR_CFG bit definitions */
+#define WX_PX_RR_CFG_VLAN            BIT(31)
 #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)
 #define WX_MAX_TXD                   8192
 
 #define WX_MAX_JUMBO_FRAME_SIZE      9432 /* max payload 9414 */
+#define VMDQ_P(p)                    p
 
 /* Supported Rx Buffer Sizes */
 #define WX_RXBUFFER_256      256    /* Used for skb receive header */
 /******************* Receive Descriptor bit definitions **********************/
 #define WX_RXD_STAT_DD               BIT(0) /* Done */
 #define WX_RXD_STAT_EOP              BIT(1) /* End of Packet */
+#define WX_RXD_STAT_VP               BIT(5) /* IEEE VLAN Pkt */
 #define WX_RXD_STAT_L4CS             BIT(7) /* L4 xsum calculated */
 #define WX_RXD_STAT_IPCS             BIT(8) /* IP xsum calculated */
 #define WX_RXD_STAT_OUTERIPCS        BIT(10) /* Cloud IP xsum calculated*/
@@ -566,6 +586,8 @@ struct wx_mac_info {
        u32 mta_shadow[128];
        s32 mc_filter_type;
        u32 mcft_size;
+       u32 vft_shadow[128];
+       u32 vft_size;
        u32 num_rar_entries;
        u32 rx_pb_size;
        u32 tx_pb_size;
@@ -726,7 +748,6 @@ struct wx_ring_container {
        u8 count;                       /* total number of rings in vector */
        u8 itr;                         /* current ITR setting for ring */
 };
-
 struct wx_ring {
        struct wx_ring *next;           /* pointer to next ring in q_vector */
        struct wx_q_vector *q_vector;   /* backpointer to host q_vector */
@@ -789,6 +810,8 @@ enum wx_isb_idx {
 };
 
 struct wx {
+       unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
+
        u8 __iomem *hw_addr;
        struct pci_dev *pdev;
        struct net_device *netdev;