]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
drivers: net: cpsw: enable promiscuous mode support
authorMugunthan V N <mugunthanvnm@ti.com>
Wed, 22 Jan 2014 18:33:12 +0000 (00:03 +0530)
committerDavid S. Miller <davem@davemloft.net>
Thu, 23 Jan 2014 21:12:14 +0000 (13:12 -0800)
Enable promiscuous mode support for CPSW.

Signed-off-by: Mugunthan V N <mugunthanvnm@ti.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 e8bb77d25d987bc15fbd9b90bc6d307a1ba889e7..bde63e3af96f6a0e005cd2cc9da93b83a53dc0db 100644 (file)
@@ -541,14 +541,93 @@ static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
                return slave_num;
 }
 
+static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
+{
+       struct cpsw_priv *priv = netdev_priv(ndev);
+       struct cpsw_ale *ale = priv->ale;
+       int i;
+
+       if (priv->data.dual_emac) {
+               bool flag = false;
+
+               /* Enabling promiscuous mode for one interface will be
+                * common for both the interface as the interface shares
+                * the same hardware resource.
+                */
+               for (i = 0; i <= priv->data.slaves; i++)
+                       if (priv->slaves[i].ndev->flags & IFF_PROMISC)
+                               flag = true;
+
+               if (!enable && flag) {
+                       enable = true;
+                       dev_err(&ndev->dev, "promiscuity not disabled as the other interface is still in promiscuity mode\n");
+               }
+
+               if (enable) {
+                       /* Enable Bypass */
+                       cpsw_ale_control_set(ale, 0, ALE_BYPASS, 1);
+
+                       dev_dbg(&ndev->dev, "promiscuity enabled\n");
+               } else {
+                       /* Disable Bypass */
+                       cpsw_ale_control_set(ale, 0, ALE_BYPASS, 0);
+                       dev_dbg(&ndev->dev, "promiscuity disabled\n");
+               }
+       } else {
+               if (enable) {
+                       unsigned long timeout = jiffies + HZ;
+
+                       /* Disable Learn for all ports */
+                       for (i = 0; i <= priv->data.slaves; i++) {
+                               cpsw_ale_control_set(ale, i,
+                                                    ALE_PORT_NOLEARN, 1);
+                               cpsw_ale_control_set(ale, i,
+                                                    ALE_PORT_NO_SA_UPDATE, 1);
+                       }
+
+                       /* Clear All Untouched entries */
+                       cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
+                       do {
+                               cpu_relax();
+                               if (cpsw_ale_control_get(ale, 0, ALE_AGEOUT))
+                                       break;
+                       } while (time_after(timeout, jiffies));
+                       cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
+
+                       /* Clear all mcast from ALE */
+                       cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS <<
+                                                priv->host_port);
+
+                       /* Flood All Unicast Packets to Host port */
+                       cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1);
+                       dev_dbg(&ndev->dev, "promiscuity enabled\n");
+               } else {
+                       /* Flood All Unicast Packets to Host port */
+                       cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 0);
+
+                       /* Enable Learn for all ports */
+                       for (i = 0; i <= priv->data.slaves; i++) {
+                               cpsw_ale_control_set(ale, i,
+                                                    ALE_PORT_NOLEARN, 0);
+                               cpsw_ale_control_set(ale, i,
+                                                    ALE_PORT_NO_SA_UPDATE, 0);
+                       }
+                       dev_dbg(&ndev->dev, "promiscuity disabled\n");
+               }
+       }
+}
+
 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");
+               cpsw_set_promiscious(ndev, true);
                return;
+       } else {
+               /* Disable promiscuous mode */
+               cpsw_set_promiscious(ndev, false);
        }
 
        /* Clear all mcast from ALE */
@@ -1257,29 +1336,6 @@ fail:
        return NETDEV_TX_BUSY;
 }
 
-static void cpsw_ndo_change_rx_flags(struct net_device *ndev, int flags)
-{
-       /*
-        * The switch cannot operate in promiscuous mode without substantial
-        * headache.  For promiscuous mode to work, we would need to put the
-        * ALE in bypass mode and route all traffic to the host port.
-        * Subsequently, the host will need to operate as a "bridge", learn,
-        * and flood as needed.  For now, we simply complain here and
-        * do nothing about it :-)
-        */
-       if ((flags & IFF_PROMISC) && (ndev->flags & IFF_PROMISC))
-               dev_err(&ndev->dev, "promiscuity ignored!\n");
-
-       /*
-        * The switch cannot filter multicast traffic unless it is configured
-        * in "VLAN Aware" mode.  Unfortunately, VLAN awareness requires a
-        * whole bunch of additional logic that this driver does not implement
-        * at present.
-        */
-       if ((flags & IFF_ALLMULTI) && !(ndev->flags & IFF_ALLMULTI))
-               dev_err(&ndev->dev, "multicast traffic cannot be filtered!\n");
-}
-
 #ifdef CONFIG_TI_CPTS
 
 static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
@@ -1575,7 +1631,6 @@ static const struct net_device_ops cpsw_netdev_ops = {
        .ndo_open               = cpsw_ndo_open,
        .ndo_stop               = cpsw_ndo_stop,
        .ndo_start_xmit         = cpsw_ndo_start_xmit,
-       .ndo_change_rx_flags    = cpsw_ndo_change_rx_flags,
        .ndo_set_mac_address    = cpsw_ndo_set_mac_address,
        .ndo_do_ioctl           = cpsw_ndo_ioctl,
        .ndo_validate_addr      = eth_validate_addr,
index 63e981975059677c2715c72971bba04d94b0e5da..7f893069c4187caab2e8071775f1d0756eb5a17a 100644 (file)
@@ -477,6 +477,14 @@ static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
                .port_shift     = 0,
                .bits           = 1,
        },
+       [ALE_P0_UNI_FLOOD]      = {
+               .name           = "port0_unicast_flood",
+               .offset         = ALE_CONTROL,
+               .port_offset    = 0,
+               .shift          = 8,
+               .port_shift     = 0,
+               .bits           = 1,
+       },
        [ALE_VLAN_NOLEARN]      = {
                .name           = "vlan_nolearn",
                .offset         = ALE_CONTROL,
@@ -573,6 +581,14 @@ static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
                .port_shift     = 0,
                .bits           = 1,
        },
+       [ALE_PORT_NO_SA_UPDATE] = {
+               .name           = "no_source_update",
+               .offset         = ALE_PORTCTL,
+               .port_offset    = 4,
+               .shift          = 5,
+               .port_shift     = 0,
+               .bits           = 1,
+       },
        [ALE_PORT_MCAST_LIMIT]  = {
                .name           = "mcast_limit",
                .offset         = ALE_PORTCTL,
index 30daa1265f0c27c1d277a0d953d9d73c6e1995cf..de409c33b2502556366c2843ba8901f361bae87d 100644 (file)
@@ -34,6 +34,7 @@ enum cpsw_ale_control {
        ALE_ENABLE,
        ALE_CLEAR,
        ALE_AGEOUT,
+       ALE_P0_UNI_FLOOD,
        ALE_VLAN_NOLEARN,
        ALE_NO_PORT_VLAN,
        ALE_OUI_DENY,
@@ -47,6 +48,7 @@ enum cpsw_ale_control {
        ALE_PORT_DROP_UNTAGGED,
        ALE_PORT_DROP_UNKNOWN_VLAN,
        ALE_PORT_NOLEARN,
+       ALE_PORT_NO_SA_UPDATE,
        ALE_PORT_UNKNOWN_VLAN_MEMBER,
        ALE_PORT_UNKNOWN_MCAST_FLOOD,
        ALE_PORT_UNKNOWN_REG_MCAST_FLOOD,