net/axgbe: support VLAN
[dpdk.git] / drivers / net / axgbe / axgbe_dev.c
index af62eae..786288a 100644 (file)
@@ -8,6 +8,46 @@
 #include "axgbe_phy.h"
 #include "axgbe_rxtx.h"
 
+static uint32_t bitrev32(uint32_t x)
+{
+       x = (x >> 16) | (x << 16);
+       x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
+       x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
+       x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
+       x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
+       return x;
+}
+
+/*MSB set bit from 32 to 1*/
+static int get_lastbit_set(int x)
+{
+       int r = 32;
+
+       if (!x)
+               return 0;
+       if (!(x & 0xffff0000)) {
+               x <<= 16;
+               r -= 16;
+       }
+       if (!(x & 0xff000000)) {
+               x <<= 8;
+               r -= 8;
+       }
+       if (!(x & 0xf0000000)) {
+               x <<= 4;
+               r -= 4;
+       }
+       if (!(x & 0xc0000000)) {
+               x <<= 2;
+               r -= 2;
+       }
+       if (!(x & 0x80000000)) {
+               x <<= 1;
+               r -= 1;
+       }
+       return r;
+}
+
 static inline unsigned int axgbe_get_max_frame(struct axgbe_port *pdata)
 {
        return pdata->eth_dev->data->mtu + RTE_ETHER_HDR_LEN +
@@ -407,6 +447,120 @@ static void axgbe_config_flow_control_threshold(struct axgbe_port *pdata)
        }
 }
 
+static int axgbe_enable_rx_vlan_stripping(struct axgbe_port *pdata)
+{
+       /* Put the VLAN tag in the Rx descriptor */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLRXS, 1);
+
+       /* Don't check the VLAN type */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, DOVLTC, 1);
+
+       /* Check only C-TAG (0x8100) packets */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ERSVLM, 0);
+
+       /* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, ESVL, 0);
+
+       /* Enable VLAN tag stripping */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0x3);
+       return 0;
+}
+
+static int axgbe_disable_rx_vlan_stripping(struct axgbe_port *pdata)
+{
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, EVLS, 0);
+       return 0;
+}
+
+static int axgbe_enable_rx_vlan_filtering(struct axgbe_port *pdata)
+{
+       /* Enable VLAN filtering */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_PFR, VTFE, 1);
+
+       /* Enable VLAN Hash Table filtering */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VTHM, 1);
+
+       /* Disable VLAN tag inverse matching */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VTIM, 0);
+
+       /* Only filter on the lower 12-bits of the VLAN tag */
+       AXGMAC_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.
+        */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANTR, VL, 1);
+       return 0;
+}
+
+static int axgbe_disable_rx_vlan_filtering(struct axgbe_port *pdata)
+{
+       /* Disable VLAN filtering */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_PFR, VTFE, 0);
+       return 0;
+}
+
+static u32 axgbe_vid_crc32_le(__le16 vid_le)
+{
+       u32 poly = 0xedb88320;  /* 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_lastbit_set(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 axgbe_update_vlan_hash_table(struct axgbe_port *pdata)
+{
+       u32 crc = 0;
+       u16 vid;
+       __le16 vid_le = 0;
+       u16 vlan_hash_table = 0;
+       unsigned int reg = 0;
+       unsigned long vid_idx, vid_valid;
+
+       /* Generate the VLAN Hash Table value */
+       for (vid = 0; vid < VLAN_N_VID; vid++) {
+               vid_idx = VLAN_TABLE_IDX(vid);
+               vid_valid = pdata->active_vlans[vid_idx];
+               vid_valid = (unsigned long)vid_valid >> (vid - (64 * vid_idx));
+               if (vid_valid & 1)
+                       PMD_DRV_LOG(DEBUG,
+                                   "vid:%d pdata->active_vlans[%ld]=0x%lx\n",
+                                   vid, vid_idx, pdata->active_vlans[vid_idx]);
+               else
+                       continue;
+
+               vid_le = rte_cpu_to_le_16(vid);
+               crc = bitrev32(~axgbe_vid_crc32_le(vid_le)) >> 28;
+               vlan_hash_table |= (1 << crc);
+               PMD_DRV_LOG(DEBUG, "crc = %d vlan_hash_table = 0x%x\n",
+                           crc, vlan_hash_table);
+       }
+       /* Set the VLAN Hash Table filtering register */
+       AXGMAC_IOWRITE_BITS(pdata, MAC_VLANHTR, VLHT, vlan_hash_table);
+       reg = AXGMAC_IOREAD(pdata, MAC_VLANHTR);
+       PMD_DRV_LOG(DEBUG, "vlan_hash_table reg val = 0x%x\n", reg);
+       return 0;
+}
+
 static int __axgbe_exit(struct axgbe_port *pdata)
 {
        unsigned int count = 2000;
@@ -1009,16 +1163,6 @@ static void axgbe_enable_mtl_interrupts(struct axgbe_port *pdata)
        }
 }
 
-static uint32_t bitrev32(uint32_t x)
-{
-       x = (x >> 16) | (x << 16);
-       x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
-       x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
-       x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
-       x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
-       return x;
-}
-
 static uint32_t crc32_le(uint32_t crc, uint8_t *p, uint32_t len)
 {
        int i;
@@ -1218,4 +1362,11 @@ void axgbe_init_function_ptrs_dev(struct axgbe_hw_if *hw_if)
        /* For FLOW ctrl */
        hw_if->config_tx_flow_control = axgbe_config_tx_flow_control;
        hw_if->config_rx_flow_control = axgbe_config_rx_flow_control;
+
+       /*vlan*/
+       hw_if->enable_rx_vlan_stripping = axgbe_enable_rx_vlan_stripping;
+       hw_if->disable_rx_vlan_stripping = axgbe_disable_rx_vlan_stripping;
+       hw_if->enable_rx_vlan_filtering = axgbe_enable_rx_vlan_filtering;
+       hw_if->disable_rx_vlan_filtering = axgbe_disable_rx_vlan_filtering;
+       hw_if->update_vlan_hash_table = axgbe_update_vlan_hash_table;
 }