Merge remote-tracking branches 'asoc/fix/blackfin', 'asoc/fix/da9055', 'asoc/fix...
[firefly-linux-kernel-4.4.55.git] / drivers / net / ethernet / ti / cpsw.c
index 5330fd298705e06181b7d1a2a1557e0cea27e85c..1d860ce914edefabba03f681a1d44a48c5c4911e 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 */
@@ -582,7 +661,7 @@ static void cpsw_intr_disable(struct cpsw_priv *priv)
        return;
 }
 
-void cpsw_tx_handler(void *token, int len, int status)
+static void cpsw_tx_handler(void *token, int len, int status)
 {
        struct sk_buff          *skb = token;
        struct net_device       *ndev = skb->dev;
@@ -599,7 +678,7 @@ void cpsw_tx_handler(void *token, int len, int status)
        dev_kfree_skb_any(skb);
 }
 
-void cpsw_rx_handler(void *token, int len, int status)
+static void cpsw_rx_handler(void *token, int len, int status)
 {
        struct sk_buff          *skb = token;
        struct sk_buff          *new_skb;
@@ -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)
@@ -1331,7 +1387,7 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv)
        __raw_writel(ETH_P_1588, &priv->regs->ts_ltype);
 }
 
-static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
+static int cpsw_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 {
        struct cpsw_priv *priv = netdev_priv(dev);
        struct cpts *cpts = priv->cpts;
@@ -1392,6 +1448,24 @@ static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr)
        return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
 }
 
+static int cpsw_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
+{
+       struct cpsw_priv *priv = netdev_priv(dev);
+       struct cpts *cpts = priv->cpts;
+       struct hwtstamp_config cfg;
+
+       if (priv->version != CPSW_VERSION_1 &&
+           priv->version != CPSW_VERSION_2)
+               return -EOPNOTSUPP;
+
+       cfg.flags = 0;
+       cfg.tx_type = cpts->tx_enable ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       cfg.rx_filter = (cpts->rx_enable ?
+                        HWTSTAMP_FILTER_PTP_V2_EVENT : HWTSTAMP_FILTER_NONE);
+
+       return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+}
+
 #endif /*CONFIG_TI_CPTS*/
 
 static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
@@ -1406,7 +1480,9 @@ static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
        switch (cmd) {
 #ifdef CONFIG_TI_CPTS
        case SIOCSHWTSTAMP:
-               return cpsw_hwtstamp_ioctl(dev, req);
+               return cpsw_hwtstamp_set(dev, req);
+       case SIOCGHWTSTAMP:
+               return cpsw_hwtstamp_get(dev, req);
 #endif
        case SIOCGMIIPHY:
                data->phy_id = priv->slaves[slave_no].phy->addr;
@@ -1555,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,
@@ -1803,8 +1878,18 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
                mdio_node = of_find_node_by_phandle(be32_to_cpup(parp));
                phyid = be32_to_cpup(parp+1);
                mdio = of_find_device_by_node(mdio_node);
-               snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
-                        PHY_ID_FMT, mdio->name, phyid);
+
+               if (strncmp(mdio->name, "gpio", 4) == 0) {
+                       /* GPIO bitbang MDIO driver attached */
+                       struct mii_bus *bus = dev_get_drvdata(&mdio->dev);
+
+                       snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
+                                PHY_ID_FMT, bus->id, phyid);
+               } else {
+                       /* davinci MDIO driver attached */
+                       snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
+                                PHY_ID_FMT, mdio->name, phyid);
+               }
 
                mac_addr = of_get_mac_address(slave_node);
                if (mac_addr)
@@ -2137,8 +2222,8 @@ static int cpsw_probe(struct platform_device *pdev)
                          data->cpts_clock_mult, data->cpts_clock_shift))
                dev_err(priv->dev, "error registering cpts device\n");
 
-       cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n",
-                   ss_res->start, ndev->irq);
+       cpsw_notice(priv, probe, "initialized device (regs %pa, irq %d)\n",
+                   &ss_res->start, ndev->irq);
 
        if (priv->data.dual_emac) {
                ret = cpsw_probe_dual_emac(pdev, priv);