amd-xgbe: Change destination address filtering support
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Tue, 24 Jun 2014 21:19:29 +0000 (16:19 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 27 Jun 2014 00:14:04 +0000 (17:14 -0700)
Currently the driver makes use of the additional mac address
registers in the hardware to provide perfect filtering.  The
hardware can also have a set of hash table registers that can
be used for imperfect filtering.  By using imperfect filtering
the additional mac address registers can be used for layer 2
filtering support.  Use the hash table registers if the device
has them.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/amd/Kconfig
drivers/net/ethernet/amd/xgbe/xgbe-common.h
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe.h

index 8e83e4c8fff007751429ae05988ad64127d526e6..6e314dbba80505e353b01c2f6e0052ca407f8231 100644 (file)
@@ -183,6 +183,7 @@ config AMD_XGBE
        select PHYLIB
        select AMD_XGBE_PHY
        select BITREVERSE
+       select CRC32
        ---help---
          This driver supports the AMD 10GbE Ethernet device found on an
          AMD SoC.
index 1de9d0fc594af0a220c758e935c940bb07e94a7b..ccbceba553e5e1ef5cd970237643f64671888359 100644 (file)
 #define MAC_PFR                                0x0008
 #define MAC_WTR                                0x000c
 #define MAC_HTR0                       0x0010
-#define MAC_HTR1                       0x0014
-#define MAC_HTR2                       0x0018
-#define MAC_HTR3                       0x001c
-#define MAC_HTR4                       0x0020
-#define MAC_HTR5                       0x0024
-#define MAC_HTR6                       0x0028
-#define MAC_HTR7                       0x002c
 #define MAC_VLANTR                     0x0050
 #define MAC_VLANHTR                    0x0058
 #define MAC_VLANIR                     0x0060
 
 #define MAC_QTFCR_INC                  4
 #define MAC_MACA_INC                   4
+#define MAC_HTR_INC                    4
 
 /* MAC register entry bit positions and sizes */
 #define MAC_HWF0R_ADDMACADRSEL_INDEX   18
 #define MAC_MACA1HR_AE_WIDTH           1
 #define MAC_PFR_HMC_INDEX              2
 #define MAC_PFR_HMC_WIDTH              1
+#define MAC_PFR_HPF_INDEX              10
+#define MAC_PFR_HPF_WIDTH              1
 #define MAC_PFR_HUC_INDEX              1
 #define MAC_PFR_HUC_WIDTH              1
 #define MAC_PFR_PM_INDEX               4
index 2c7d582e0859b3bf567c6964ca376bffab856c92..a56069c91fc4a0305f08e33c1f384a03851acbda 100644 (file)
 #include <linux/phy.h>
 #include <linux/clk.h>
 #include <linux/bitrev.h>
+#include <linux/crc32.h>
 
 #include "xgbe.h"
 #include "xgbe-common.h"
@@ -548,24 +549,16 @@ static int xgbe_set_all_multicast_mode(struct xgbe_prv_data *pdata,
        return 0;
 }
 
-static int xgbe_set_addn_mac_addrs(struct xgbe_prv_data *pdata,
-                                  unsigned int am_mode)
+static void xgbe_set_mac_reg(struct xgbe_prv_data *pdata,
+                            struct netdev_hw_addr *ha, unsigned int *mac_reg)
 {
-       struct netdev_hw_addr *ha;
-       unsigned int mac_reg;
        unsigned int mac_addr_hi, mac_addr_lo;
        u8 *mac_addr;
-       unsigned int i;
 
-       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HUC, 0);
-       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HMC, 0);
-
-       i = 0;
-       mac_reg = MAC_MACA1HR;
+       mac_addr_lo = 0;
+       mac_addr_hi = 0;
 
-       netdev_for_each_uc_addr(ha, pdata->netdev) {
-               mac_addr_lo = 0;
-               mac_addr_hi = 0;
+       if (ha) {
                mac_addr = (u8 *)&mac_addr_lo;
                mac_addr[0] = ha->addr[0];
                mac_addr[1] = ha->addr[1];
@@ -575,54 +568,93 @@ static int xgbe_set_addn_mac_addrs(struct xgbe_prv_data *pdata,
                mac_addr[0] = ha->addr[4];
                mac_addr[1] = ha->addr[5];
 
-               DBGPR("  adding unicast address %pM at 0x%04x\n",
-                     ha->addr, mac_reg);
+               DBGPR("  adding mac address %pM at 0x%04x\n", ha->addr,
+                     *mac_reg);
 
                XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1);
+       }
 
-               XGMAC_IOWRITE(pdata, mac_reg, mac_addr_hi);
-               mac_reg += MAC_MACA_INC;
-               XGMAC_IOWRITE(pdata, mac_reg, mac_addr_lo);
-               mac_reg += MAC_MACA_INC;
+       XGMAC_IOWRITE(pdata, *mac_reg, mac_addr_hi);
+       *mac_reg += MAC_MACA_INC;
+       XGMAC_IOWRITE(pdata, *mac_reg, mac_addr_lo);
+       *mac_reg += MAC_MACA_INC;
+}
 
-               i++;
-       }
+static void xgbe_set_mac_addn_addrs(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+       struct netdev_hw_addr *ha;
+       unsigned int mac_reg;
+       unsigned int addn_macs;
+
+       mac_reg = MAC_MACA1HR;
+       addn_macs = pdata->hw_feat.addn_mac;
 
-       if (!am_mode) {
-               netdev_for_each_mc_addr(ha, pdata->netdev) {
-                       mac_addr_lo = 0;
-                       mac_addr_hi = 0;
-                       mac_addr = (u8 *)&mac_addr_lo;
-                       mac_addr[0] = ha->addr[0];
-                       mac_addr[1] = ha->addr[1];
-                       mac_addr[2] = ha->addr[2];
-                       mac_addr[3] = ha->addr[3];
-                       mac_addr = (u8 *)&mac_addr_hi;
-                       mac_addr[0] = ha->addr[4];
-                       mac_addr[1] = ha->addr[5];
-
-                       DBGPR("  adding multicast address %pM at 0x%04x\n",
-                             ha->addr, mac_reg);
-
-                       XGMAC_SET_BITS(mac_addr_hi, MAC_MACA1HR, AE, 1);
-
-                       XGMAC_IOWRITE(pdata, mac_reg, mac_addr_hi);
-                       mac_reg += MAC_MACA_INC;
-                       XGMAC_IOWRITE(pdata, mac_reg, mac_addr_lo);
-                       mac_reg += MAC_MACA_INC;
-
-                       i++;
+       if (netdev_uc_count(netdev) > addn_macs) {
+               xgbe_set_promiscuous_mode(pdata, 1);
+       } else {
+               netdev_for_each_uc_addr(ha, netdev) {
+                       xgbe_set_mac_reg(pdata, ha, &mac_reg);
+                       addn_macs--;
+               }
+
+               if (netdev_mc_count(netdev) > addn_macs) {
+                       xgbe_set_all_multicast_mode(pdata, 1);
+               } else {
+                       netdev_for_each_mc_addr(ha, netdev) {
+                               xgbe_set_mac_reg(pdata, ha, &mac_reg);
+                               addn_macs--;
+                       }
                }
        }
 
        /* Clear remaining additional MAC address entries */
-       for (; i < pdata->hw_feat.addn_mac; i++) {
-               XGMAC_IOWRITE(pdata, mac_reg, 0);
-               mac_reg += MAC_MACA_INC;
-               XGMAC_IOWRITE(pdata, mac_reg, 0);
-               mac_reg += MAC_MACA_INC;
+       while (addn_macs--)
+               xgbe_set_mac_reg(pdata, NULL, &mac_reg);
+}
+
+static void xgbe_set_mac_hash_table(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+       struct netdev_hw_addr *ha;
+       unsigned int hash_reg;
+       unsigned int hash_table_shift, hash_table_count;
+       u32 hash_table[XGBE_MAC_HASH_TABLE_SIZE];
+       u32 crc;
+       unsigned int i;
+
+       hash_table_shift = 26 - (pdata->hw_feat.hash_table_size >> 7);
+       hash_table_count = pdata->hw_feat.hash_table_size / 32;
+       memset(hash_table, 0, sizeof(hash_table));
+
+       /* Build the MAC Hash Table register values */
+       netdev_for_each_uc_addr(ha, netdev) {
+               crc = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN));
+               crc >>= hash_table_shift;
+               hash_table[crc >> 5] |= (1 << (crc & 0x1f));
+       }
+
+       netdev_for_each_mc_addr(ha, netdev) {
+               crc = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN));
+               crc >>= hash_table_shift;
+               hash_table[crc >> 5] |= (1 << (crc & 0x1f));
        }
 
+       /* Set the MAC Hash Table registers */
+       hash_reg = MAC_HTR0;
+       for (i = 0; i < hash_table_count; i++) {
+               XGMAC_IOWRITE(pdata, hash_reg, hash_table[i]);
+               hash_reg += MAC_HTR_INC;
+       }
+}
+
+static int xgbe_add_mac_addresses(struct xgbe_prv_data *pdata)
+{
+       if (pdata->hw_feat.hash_table_size)
+               xgbe_set_mac_hash_table(pdata);
+       else
+               xgbe_set_mac_addn_addrs(pdata);
+
        return 0;
 }
 
@@ -1606,6 +1638,13 @@ static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata)
 static void xgbe_config_mac_address(struct xgbe_prv_data *pdata)
 {
        xgbe_set_mac_address(pdata, pdata->netdev->dev_addr);
+
+       /* Filtering is done using perfect filtering and hash filtering */
+       if (pdata->hw_feat.hash_table_size) {
+               XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HPF, 1);
+               XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HUC, 1);
+               XGMAC_IOWRITE_BITS(pdata, MAC_PFR, HMC, 1);
+       }
 }
 
 static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata)
@@ -2202,7 +2241,7 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
 
        hw_if->set_promiscuous_mode = xgbe_set_promiscuous_mode;
        hw_if->set_all_multicast_mode = xgbe_set_all_multicast_mode;
-       hw_if->set_addn_mac_addrs = xgbe_set_addn_mac_addrs;
+       hw_if->add_mac_addresses = xgbe_add_mac_addresses;
        hw_if->set_mac_address = xgbe_set_mac_address;
 
        hw_if->enable_rx_csum = xgbe_enable_rx_csum;
index 2acc37c07d9b221217e6e7253002b5dafddb212f..72dd61166a1c696c3f6b96365744d4d39626ce7b 100644 (file)
@@ -378,6 +378,21 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
        hw_feat->pps_out_num  = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, PPSOUTNUM);
        hw_feat->aux_snap_num = XGMAC_GET_BITS(mac_hfr2, MAC_HWF2R, AUXSNAPNUM);
 
+       /* Translate the Hash Table size into actual number */
+       switch (hw_feat->hash_table_size) {
+       case 0:
+               break;
+       case 1:
+               hw_feat->hash_table_size = 64;
+               break;
+       case 2:
+               hw_feat->hash_table_size = 128;
+               break;
+       case 3:
+               hw_feat->hash_table_size = 256;
+               break;
+       }
+
        /* The Queue and Channel counts are zero based so increment them
         * to get the actual number
         */
@@ -912,18 +927,10 @@ static void xgbe_set_rx_mode(struct net_device *netdev)
        pr_mode = ((netdev->flags & IFF_PROMISC) != 0);
        am_mode = ((netdev->flags & IFF_ALLMULTI) != 0);
 
-       if (netdev_uc_count(netdev) > pdata->hw_feat.addn_mac)
-               pr_mode = 1;
-       if (netdev_mc_count(netdev) > pdata->hw_feat.addn_mac)
-               am_mode = 1;
-       if ((netdev_uc_count(netdev) + netdev_mc_count(netdev)) >
-            pdata->hw_feat.addn_mac)
-               pr_mode = 1;
-
        hw_if->set_promiscuous_mode(pdata, pr_mode);
        hw_if->set_all_multicast_mode(pdata, am_mode);
-       if (!pr_mode)
-               hw_if->set_addn_mac_addrs(pdata, am_mode);
+
+       hw_if->add_mac_addresses(pdata);
 
        DBGPR("<--xgbe_set_rx_mode\n");
 }
index c3a48b7c2aed80dadef7ee2e361a857ae4d05b6b..b411ac557c47f787dad81263bc1595e848197a6e 100644 (file)
@@ -397,6 +397,8 @@ static int xgbe_probe(struct platform_device *pdev)
        netdev->features |= netdev->hw_features;
        pdata->netdev_features = netdev->features;
 
+       netdev->priv_flags |= IFF_UNICAST_FLT;
+
        xgbe_init_rx_coalesce(pdata);
        xgbe_init_tx_coalesce(pdata);
 
index 3cb911f39b60ef559c6731d1ffa13d5391cb5585..a2d5f5f5d8b773e518daed5e783ab6368e908101 100644 (file)
 /* Flow control queue count */
 #define XGMAC_MAX_FLOW_CONTROL_QUEUES  8
 
+/* Maximum MAC address hash table size (256 bits = 8 bytes) */
+#define XGBE_MAC_HASH_TABLE_SIZE       8
 
 struct xgbe_prv_data;
 
@@ -387,7 +389,7 @@ struct xgbe_hw_if {
 
        int (*set_promiscuous_mode)(struct xgbe_prv_data *, unsigned int);
        int (*set_all_multicast_mode)(struct xgbe_prv_data *, unsigned int);
-       int (*set_addn_mac_addrs)(struct xgbe_prv_data *, unsigned int);
+       int (*add_mac_addresses)(struct xgbe_prv_data *);
        int (*set_mac_address)(struct xgbe_prv_data *, u8 *addr);
 
        int (*enable_rx_csum)(struct xgbe_prv_data *);