|
@@ -541,14 +541,93 @@ static inline int cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)
|
|
return 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)
|
|
static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
|
|
{
|
|
{
|
|
struct cpsw_priv *priv = netdev_priv(ndev);
|
|
struct cpsw_priv *priv = netdev_priv(ndev);
|
|
|
|
|
|
if (ndev->flags & IFF_PROMISC) {
|
|
if (ndev->flags & IFF_PROMISC) {
|
|
/* Enable promiscuous mode */
|
|
/* Enable promiscuous mode */
|
|
- dev_err(priv->dev, "Ignoring Promiscuous mode\n");
|
|
|
|
|
|
+ cpsw_set_promiscious(ndev, true);
|
|
return;
|
|
return;
|
|
|
|
+ } else {
|
|
|
|
+ /* Disable promiscuous mode */
|
|
|
|
+ cpsw_set_promiscious(ndev, false);
|
|
}
|
|
}
|
|
|
|
|
|
/* Clear all mcast from ALE */
|
|
/* Clear all mcast from ALE */
|
|
@@ -1257,29 +1336,6 @@ fail:
|
|
return NETDEV_TX_BUSY;
|
|
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
|
|
#ifdef CONFIG_TI_CPTS
|
|
|
|
|
|
static void cpsw_hwtstamp_v1(struct cpsw_priv *priv)
|
|
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_open = cpsw_ndo_open,
|
|
.ndo_stop = cpsw_ndo_stop,
|
|
.ndo_stop = cpsw_ndo_stop,
|
|
.ndo_start_xmit = cpsw_ndo_start_xmit,
|
|
.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_set_mac_address = cpsw_ndo_set_mac_address,
|
|
.ndo_do_ioctl = cpsw_ndo_ioctl,
|
|
.ndo_do_ioctl = cpsw_ndo_ioctl,
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
.ndo_validate_addr = eth_validate_addr,
|