aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet
diff options
context:
space:
mode:
authorLennart Sorensen <lsorense@csclub.uwaterloo.ca>2014-10-31 13:38:52 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-31 16:16:42 -0400
commit10738eeaf4ab3de092586cefcc082e7d43ca0044 (patch)
tree3a4bc1fda702c9ab295495c5ab469e2157927fcc /drivers/net/ethernet
parent35532c211b1feda52078685bef0bf9b5f3b0991c (diff)
drivers: net: cpsw: Support ALLMULTI and fix IFF_PROMISC in switch mode
The cpsw driver did not support the IFF_ALLMULTI flag which makes dynamic multicast routing not work. Related to this, when enabling IFF_PROMISC in switch mode, all registered multicast addresses are flushed, resulting in only broadcast and unicast traffic being received. A new cpsw_ale_set_allmulti function now scans through the ALE entry table and adds/removes the host port from the unregistered multicast port mask of each vlan entry depending on the state of IFF_ALLMULTI. In promiscious mode, cpsw_ale_set_allmulti is used to force reception of all multicast traffic in addition to the unicast and broadcast traffic. With this change dynamic multicast and promiscious mode both work in switch mode. Signed-off-by: Len Sorensen <lsorense@csclub.uwaterloo.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r--drivers/net/ethernet/ti/cpsw.c20
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.c29
-rw-r--r--drivers/net/ethernet/ti/cpsw_ale.h2
3 files changed, 49 insertions, 2 deletions
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 4683196c6592..96a61d169215 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -638,12 +638,16 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
638 if (ndev->flags & IFF_PROMISC) { 638 if (ndev->flags & IFF_PROMISC) {
639 /* Enable promiscuous mode */ 639 /* Enable promiscuous mode */
640 cpsw_set_promiscious(ndev, true); 640 cpsw_set_promiscious(ndev, true);
641 cpsw_ale_set_allmulti(priv->ale, IFF_ALLMULTI);
641 return; 642 return;
642 } else { 643 } else {
643 /* Disable promiscuous mode */ 644 /* Disable promiscuous mode */
644 cpsw_set_promiscious(ndev, false); 645 cpsw_set_promiscious(ndev, false);
645 } 646 }
646 647
648 /* Restore allmulti on vlans if necessary */
649 cpsw_ale_set_allmulti(priv->ale, priv->ndev->flags & IFF_ALLMULTI);
650
647 /* Clear all mcast from ALE */ 651 /* Clear all mcast from ALE */
648 cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port); 652 cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port);
649 653
@@ -1149,6 +1153,7 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
1149 const int port = priv->host_port; 1153 const int port = priv->host_port;
1150 u32 reg; 1154 u32 reg;
1151 int i; 1155 int i;
1156 int unreg_mcast_mask;
1152 1157
1153 reg = (priv->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN : 1158 reg = (priv->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
1154 CPSW2_PORT_VLAN; 1159 CPSW2_PORT_VLAN;
@@ -1158,9 +1163,14 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
1158 for (i = 0; i < priv->data.slaves; i++) 1163 for (i = 0; i < priv->data.slaves; i++)
1159 slave_write(priv->slaves + i, vlan, reg); 1164 slave_write(priv->slaves + i, vlan, reg);
1160 1165
1166 if (priv->ndev->flags & IFF_ALLMULTI)
1167 unreg_mcast_mask = ALE_ALL_PORTS;
1168 else
1169 unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
1170
1161 cpsw_ale_add_vlan(priv->ale, vlan, ALE_ALL_PORTS << port, 1171 cpsw_ale_add_vlan(priv->ale, vlan, ALE_ALL_PORTS << port,
1162 ALE_ALL_PORTS << port, ALE_ALL_PORTS << port, 1172 ALE_ALL_PORTS << port, ALE_ALL_PORTS << port,
1163 (ALE_PORT_1 | ALE_PORT_2) << port); 1173 unreg_mcast_mask << port);
1164} 1174}
1165 1175
1166static void cpsw_init_host_port(struct cpsw_priv *priv) 1176static void cpsw_init_host_port(struct cpsw_priv *priv)
@@ -1620,11 +1630,17 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
1620 unsigned short vid) 1630 unsigned short vid)
1621{ 1631{
1622 int ret; 1632 int ret;
1633 int unreg_mcast_mask;
1634
1635 if (priv->ndev->flags & IFF_ALLMULTI)
1636 unreg_mcast_mask = ALE_ALL_PORTS;
1637 else
1638 unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
1623 1639
1624 ret = cpsw_ale_add_vlan(priv->ale, vid, 1640 ret = cpsw_ale_add_vlan(priv->ale, vid,
1625 ALE_ALL_PORTS << priv->host_port, 1641 ALE_ALL_PORTS << priv->host_port,
1626 0, ALE_ALL_PORTS << priv->host_port, 1642 0, ALE_ALL_PORTS << priv->host_port,
1627 (ALE_PORT_1 | ALE_PORT_2) << priv->host_port); 1643 unreg_mcast_mask << priv->host_port);
1628 if (ret != 0) 1644 if (ret != 0)
1629 return ret; 1645 return ret;
1630 1646
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
index 0579b2243bb6..3ae83879a75f 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.c
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -443,6 +443,35 @@ int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
443 return 0; 443 return 0;
444} 444}
445 445
446void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
447{
448 u32 ale_entry[ALE_ENTRY_WORDS];
449 int type, idx;
450 int unreg_mcast = 0;
451
452 /* Only bother doing the work if the setting is actually changing */
453 if (ale->allmulti == allmulti)
454 return;
455
456 /* Remember the new setting to check against next time */
457 ale->allmulti = allmulti;
458
459 for (idx = 0; idx < ale->params.ale_entries; idx++) {
460 cpsw_ale_read(ale, idx, ale_entry);
461 type = cpsw_ale_get_entry_type(ale_entry);
462 if (type != ALE_TYPE_VLAN)
463 continue;
464
465 unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
466 if (allmulti)
467 unreg_mcast |= 1;
468 else
469 unreg_mcast &= ~1;
470 cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
471 cpsw_ale_write(ale, idx, ale_entry);
472 }
473}
474
446struct ale_control_info { 475struct ale_control_info {
447 const char *name; 476 const char *name;
448 int offset, port_offset; 477 int offset, port_offset;
diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h
index 31cf43cab42e..c0d4127aa549 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.h
+++ b/drivers/net/ethernet/ti/cpsw_ale.h
@@ -27,6 +27,7 @@ struct cpsw_ale {
27 struct cpsw_ale_params params; 27 struct cpsw_ale_params params;
28 struct timer_list timer; 28 struct timer_list timer;
29 unsigned long ageout; 29 unsigned long ageout;
30 int allmulti;
30}; 31};
31 32
32enum cpsw_ale_control { 33enum cpsw_ale_control {
@@ -103,6 +104,7 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
103int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, 104int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
104 int reg_mcast, int unreg_mcast); 105 int reg_mcast, int unreg_mcast);
105int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port); 106int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port);
107void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti);
106 108
107int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control); 109int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
108int cpsw_ale_control_set(struct cpsw_ale *ale, int port, 110int cpsw_ale_control_set(struct cpsw_ale *ale, int port,