From 43934e114a3a3358a570480c8974e4e52769cd11 Mon Sep 17 00:00:00 2001
From: Corentin Labbe <clabbe.montjoie@gmail.com>
Date: Tue, 25 Apr 2017 15:53:58 +0200
Subject: [PATCH] net: sun8i-emac: fix multicast

Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>
---
 drivers/net/ethernet/allwinner/sun8i-emac.c | 43 ++++++++++++++++++++++-------
 1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c
index 110412c..e02cfd8 100644
--- a/drivers/net/ethernet/allwinner/sun8i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun8i-emac.c
@@ -94,6 +94,10 @@
 #define EMAC_FRM_FLT_CTL		BIT(13)
 #define EMAC_FRM_FLT_MULTICAST		BIT(16)
 
+/* Mac address */
+#define EMAC_MAX_MACADDR		8
+#define MAC_ADDR_TYPE_DST BIT(31)
+
 /* Used in BASIC_CTL0 */
 #define EMAC_BCTL0_FD			BIT(0)
 #define EMAC_BCTL0_LOOPBACK		BIT(1)
@@ -520,9 +524,16 @@ static void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv,
 {
 	u32 v;
 
+	if (index > 7) {
+		dev_err(priv->dev, "Too many MAC addr\n");
+		return;
+	}
+
 	dev_info(priv->dev, "device MAC address slot %d %pM", index, addr);
 
 	v = (addr[5] << 8) | addr[4];
+	if (index > 0)
+		v |= MAC_ADDR_TYPE_DST;
 	writel(v, priv->base + EMAC_MACADDR_HI + index * 8);
 
 	v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
@@ -1741,21 +1752,33 @@ static void sun8i_emac_set_rx_mode(struct net_device *ndev)
 	u32 v = 0;
 	int i = 0;
 	struct netdev_hw_addr *ha;
+	int macaddrs = netdev_uc_count(ndev) + netdev_mc_count(ndev) + 1;
 
-	/* Receive all multicast frames */
-	v |= EMAC_FRM_FLT_MULTICAST;
 	/* Receive all control frames */
 	v |= EMAC_FRM_FLT_CTL;
-	if (ndev->flags & IFF_PROMISC)
-		v |= EMAC_FRM_FLT_RXALL;
-	if (netdev_uc_count(ndev) > 7) {
-		v |= EMAC_FRM_FLT_RXALL;
-	} else {
-		netdev_for_each_uc_addr(ha, ndev) {
-			i++;
-			sun8i_emac_set_macaddr(priv, ha->addr, i);
+
+        if (ndev->flags & IFF_PROMISC) {
+                v = EMAC_FRM_FLT_RXALL;
+        } else if (ndev->flags & IFF_ALLMULTI) {
+                v |= EMAC_FRM_FLT_MULTICAST;
+        } else if (macaddrs <= EMAC_MAX_MACADDR) {
+		if (!netdev_mc_empty(ndev)) {
+			netdev_for_each_mc_addr(ha, ndev) {
+				i++;
+				sun8i_emac_set_macaddr(priv, ha->addr, i);
+			}
+		}
+		if (!netdev_uc_empty(ndev)) {
+			netdev_for_each_uc_addr(ha, ndev) {
+				i++;
+				sun8i_emac_set_macaddr(priv, ha->addr, i);
+			}
 		}
+	} else {
+		netdev_info(ndev, "Too many address, switching to promiscuous\n");
+		v = EMAC_FRM_FLT_RXALL;
 	}
+
 	writel(v, priv->base + EMAC_RX_FRM_FLT);
 }