drivers: net: ethernet: cpsw: add multicast address to ALE table
authorMugunthan V N <mugunthanvnm@ti.com>
Mon, 29 Oct 2012 08:45:11 +0000 (08:45 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 1 Nov 2012 16:21:29 +0000 (12:21 -0400)
Adding multicast address to ALE table via netdev ops to subscribe, transmit
or receive multicast frames to and from the network

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_ale.h

index df55e240374646e2f076d9b4764fab6dc0a07bab..63b046fc2bbad1c952e209747ce523bda4032dbe 100644 (file)
@@ -70,6 +70,8 @@ do {                                                          \
                dev_notice(priv->dev, format, ## __VA_ARGS__);  \
 } while (0)
 
+#define ALE_ALL_PORTS          0x7
+
 #define CPSW_MAJOR_VERSION(reg)                (reg >> 8 & 0x7)
 #define CPSW_MINOR_VERSION(reg)                (reg & 0xff)
 #define CPSW_RTL_VERSION(reg)          ((reg >> 11) & 0x1f)
@@ -228,6 +230,30 @@ struct cpsw_priv {
                        (func)((priv)->slaves + idx, ##arg);    \
        } while (0)
 
+static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+
+       if (ndev->flags & IFF_PROMISC) {
+               /* Enable promiscuous mode */
+               dev_err(priv->dev, "Ignoring Promiscuous mode\n");
+               return;
+       }
+
+       /* Clear all mcast from ALE */
+       cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port);
+
+       if (!netdev_mc_empty(ndev)) {
+               struct netdev_hw_addr *ha;
+
+               /* program multicast address list into ALE register */
+               netdev_for_each_mc_addr(ha, ndev) {
+                       cpsw_ale_add_mcast(priv->ale, (u8 *)ha->addr,
+                               ALE_ALL_PORTS << priv->host_port, 0, 0);
+               }
+       }
+}
+
 static void cpsw_intr_enable(struct cpsw_priv *priv)
 {
        __raw_writel(0xFF, &priv->ss_regs->tx_en);
@@ -673,6 +699,7 @@ static const struct net_device_ops cpsw_netdev_ops = {
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_tx_timeout         = cpsw_ndo_tx_timeout,
        .ndo_get_stats          = cpsw_ndo_get_stats,
+       .ndo_set_rx_mode        = cpsw_ndo_set_rx_mode,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller    = cpsw_ndo_poll_controller,
 #endif
index ca0d48a7e508059fd35b6c7bff8cd24295caadbf..0e9ccc2cf91fefce7bda15f6c9ad4ee580e6aa5d 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/io.h>
 #include <linux/stat.h>
 #include <linux/sysfs.h>
+#include <linux/etherdevice.h>
 
 #include "cpsw_ale.h"
 
@@ -211,10 +212,34 @@ static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
        mask &= ~port_mask;
 
        /* free if only remaining port is host port */
-       if (mask == BIT(ale->params.ale_ports))
-               cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
-       else
+       if (mask)
                cpsw_ale_set_port_mask(ale_entry, mask);
+       else
+               cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
+}
+
+int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask)
+{
+       u32 ale_entry[ALE_ENTRY_WORDS];
+       int ret, idx;
+
+       for (idx = 0; idx < ale->params.ale_entries; idx++) {
+               cpsw_ale_read(ale, idx, ale_entry);
+               ret = cpsw_ale_get_entry_type(ale_entry);
+               if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
+                       continue;
+
+               if (cpsw_ale_get_mcast(ale_entry)) {
+                       u8 addr[6];
+
+                       cpsw_ale_get_addr(ale_entry, addr);
+                       if (!is_broadcast_ether_addr(addr))
+                               cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
+               }
+
+               cpsw_ale_write(ale, idx, ale_entry);
+       }
+       return 0;
 }
 
 static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry,
index a95b37beb02d56e809c15239247085304c6a2cba..2bd09cbce522dc222fb5a72f9691d368a3b57094 100644 (file)
@@ -80,6 +80,7 @@ void cpsw_ale_stop(struct cpsw_ale *ale);
 
 int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout);
 int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask);
+int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask);
 int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags);
 int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port);
 int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,