aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Herring <rob.herring@calxeda.com>2013-08-30 17:49:26 -0400
committerDavid S. Miller <davem@davemloft.net>2013-09-03 22:21:15 -0400
commit2ee68f621af280c37a6bd72fe75e47de0750f301 (patch)
tree11d3e5f85570a00841d273296d3fba50f5b92468
parentf7ea10520d7dacbc416d130c4b10505c66bf4c36 (diff)
net: calxedaxgmac: fix various errors in xgmac_set_rx_mode
Fix xgmac_set_rx_mode to handle several conditions that were not handled correctly as Lennert Buytenhek describes: If we have, say, 100 unicast addresses, and 5 multicast addresses, the unicast address count check will evaluate to true, and set use_hash to true. The multicast address check will however evaluate to false, and use_hash won't be set to true again, and XGMAC_FRAME_FILTER_HMC won't be OR'd into XGMAC_FRAME_FILTER, but since use_hash was still true from the unicast check, netdev_for_each_mc_addr() will program the multicast addresses into the hash table instead of using the MAC address registers, but since the HMC bit wasn't set, the hash table won't be checked for multicast addresses on receive, and we'll stop receiving multicast packets entirely. Also, there is no code that zeroes out MAC address registers reg..31 at the end of this function, meaning that under the right conditions, unicast/multicast addresses that were previously in the filter but were then deleted won't be cleared out. Reported-by: Lennert Buytenhek <buytenh@wantstofly.org> Signed-off-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/calxeda/xgmac.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c
index 73d3f83468c4..0cff9e3e0cb7 100644
--- a/drivers/net/ethernet/calxeda/xgmac.c
+++ b/drivers/net/ethernet/calxeda/xgmac.c
@@ -618,10 +618,15 @@ static void xgmac_set_mac_addr(void __iomem *ioaddr, unsigned char *addr,
618{ 618{
619 u32 data; 619 u32 data;
620 620
621 data = (addr[5] << 8) | addr[4] | (num ? XGMAC_ADDR_AE : 0); 621 if (addr) {
622 writel(data, ioaddr + XGMAC_ADDR_HIGH(num)); 622 data = (addr[5] << 8) | addr[4] | (num ? XGMAC_ADDR_AE : 0);
623 data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; 623 writel(data, ioaddr + XGMAC_ADDR_HIGH(num));
624 writel(data, ioaddr + XGMAC_ADDR_LOW(num)); 624 data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
625 writel(data, ioaddr + XGMAC_ADDR_LOW(num));
626 } else {
627 writel(0, ioaddr + XGMAC_ADDR_HIGH(num));
628 writel(0, ioaddr + XGMAC_ADDR_LOW(num));
629 }
625} 630}
626 631
627static void xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr, 632static void xgmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr,
@@ -1294,6 +1299,8 @@ static void xgmac_set_rx_mode(struct net_device *dev)
1294 if ((netdev_mc_count(dev) + reg - 1) > XGMAC_MAX_FILTER_ADDR) { 1299 if ((netdev_mc_count(dev) + reg - 1) > XGMAC_MAX_FILTER_ADDR) {
1295 use_hash = true; 1300 use_hash = true;
1296 value |= XGMAC_FRAME_FILTER_HMC | XGMAC_FRAME_FILTER_HPF; 1301 value |= XGMAC_FRAME_FILTER_HMC | XGMAC_FRAME_FILTER_HPF;
1302 } else {
1303 use_hash = false;
1297 } 1304 }
1298 netdev_for_each_mc_addr(ha, dev) { 1305 netdev_for_each_mc_addr(ha, dev) {
1299 if (use_hash) { 1306 if (use_hash) {
@@ -1310,6 +1317,8 @@ static void xgmac_set_rx_mode(struct net_device *dev)
1310 } 1317 }
1311 1318
1312out: 1319out:
1320 for (i = reg; i < XGMAC_MAX_FILTER_ADDR; i++)
1321 xgmac_set_mac_addr(ioaddr, NULL, reg);
1313 for (i = 0; i < XGMAC_NUM_HASH; i++) 1322 for (i = 0; i < XGMAC_NUM_HASH; i++)
1314 writel(hash_filter[i], ioaddr + XGMAC_HASH(i)); 1323 writel(hash_filter[i], ioaddr + XGMAC_HASH(i));
1315 1324