net: qfec: Add multicast support

This adds multicast support to the qfec driver. The driver
is Level 2 compliant. It can send and receive multicast traffic.

Acked-by: Kaushik Sikdar <ksikdar@qualcomm.com>
Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
diff --git a/drivers/net/qfec.c b/drivers/net/qfec.c
index 71ddcff..d554fec 100644
--- a/drivers/net/qfec.c
+++ b/drivers/net/qfec.c
@@ -704,6 +704,56 @@
 }
 
 /*
+ * set up the RX filter
+ */
+static void qfec_set_rx_mode(struct net_device *dev)
+{
+	struct qfec_priv *priv = netdev_priv(dev);
+	uint32_t filter_conf;
+	int index;
+
+	/* Clear address filter entries */
+	for (index = 1; index < MAC_ADR_MAX; ++index) {
+		qfec_reg_write(priv, MAC_ADR_HIGH_REG_N(index), 0);
+		qfec_reg_write(priv, MAC_ADR_LOW_REG_N(index), 0);
+	}
+
+	if (dev->flags & IFF_PROMISC) {
+		/* Receive all frames */
+		filter_conf = MAC_FR_FILTER_RA;
+	} else if ((dev->flags & IFF_MULTICAST) == 0) {
+		/* Unicast filtering only */
+		filter_conf = MAC_FR_FILTER_HPF;
+	} else if ((netdev_mc_count(dev) > MAC_ADR_MAX - 1) ||
+		   (dev->flags & IFF_ALLMULTI)) {
+		/* Unicast filtering is enabled, Pass all multicast frames */
+		filter_conf = MAC_FR_FILTER_HPF | MAC_FR_FILTER_PM;
+	} else {
+		struct netdev_hw_addr *ha;
+
+		/* Both unicast and multicast filtering are enabled */
+		filter_conf = MAC_FR_FILTER_HPF;
+
+		index = 1;
+
+		netdev_for_each_mc_addr(ha, dev) {
+			uint32_t high, low;
+
+			high = (1 << 31) | (ha->addr[5] << 8) | (ha->addr[4]);
+			low = (ha->addr[3] << 24) | (ha->addr[2] << 16) |
+				(ha->addr[1] << 8) | (ha->addr[0]);
+
+			qfec_reg_write(priv, MAC_ADR_HIGH_REG_N(index), high);
+			qfec_reg_write(priv, MAC_ADR_LOW_REG_N(index), low);
+
+			index++;
+		}
+	}
+
+	qfec_reg_write(priv, MAC_FR_FILTER_REG, filter_conf);
+}
+
+/*
  * reset the controller
  */
 
@@ -1782,6 +1832,7 @@
 
 	/* get/set (primary) MAC address */
 	qfec_set_adr_regs(priv, dev->dev_addr);
+	qfec_set_rx_mode(dev);
 
 	/* start phy monitor */
 	QFEC_LOG(QFEC_LOG_DBG, " %s: start timer\n", __func__);
@@ -1955,6 +2006,8 @@
 
 	QFEC_LOG(QFEC_LOG_DBG2, "qfec_stats:\n");
 
+	priv->stats.multicast = qfec_reg_read(priv, NUM_MULTCST_FRM_RCVD_G);
+
 	return &priv->stats;
 }
 
@@ -2009,6 +2062,7 @@
 	.ndo_do_ioctl           = qfec_do_ioctl,
 	.ndo_tx_timeout         = qfec_tx_timeout,
 	.ndo_set_mac_address    = qfec_set_mac_address,
+	.ndo_set_multicast_list = qfec_set_rx_mode,
 
 	.ndo_change_mtu         = eth_change_mtu,
 	.ndo_validate_addr      = eth_validate_addr,