net: dsa: add includes for ethtool and phy_fixed definitions
[firefly-linux-kernel-4.4.55.git] / net / dsa / slave.c
index e38a331111c0c111d69fddf0a7b6b91a641db21a..a851e9f1411807f849567623483b56c4479695a0 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/list.h>
 #include <linux/etherdevice.h>
 #include <linux/phy.h>
+#include <linux/phy_fixed.h>
 #include <linux/of_net.h>
 #include <linux/of_mdio.h>
 #include "dsa_priv.h"
@@ -44,7 +45,7 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds)
        ds->slave_mii_bus->write = dsa_slave_phy_write;
        snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d:%.2x",
                        ds->index, ds->pd->sw_addr);
-       ds->slave_mii_bus->parent = &ds->master_mii_bus->dev;
+       ds->slave_mii_bus->parent = ds->master_dev;
 }
 
 
@@ -62,6 +63,7 @@ static int dsa_slave_open(struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct net_device *master = p->parent->dst->master_netdev;
+       struct dsa_switch *ds = p->parent;
        int err;
 
        if (!(master->flags & IFF_UP))
@@ -84,8 +86,20 @@ static int dsa_slave_open(struct net_device *dev)
                        goto clear_allmulti;
        }
 
+       if (ds->drv->port_enable) {
+               err = ds->drv->port_enable(ds, p->port, p->phy);
+               if (err)
+                       goto clear_promisc;
+       }
+
+       if (p->phy)
+               phy_start(p->phy);
+
        return 0;
 
+clear_promisc:
+       if (dev->flags & IFF_PROMISC)
+               dev_set_promiscuity(master, 0);
 clear_allmulti:
        if (dev->flags & IFF_ALLMULTI)
                dev_set_allmulti(master, -1);
@@ -100,6 +114,10 @@ static int dsa_slave_close(struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
        struct net_device *master = p->parent->dst->master_netdev;
+       struct dsa_switch *ds = p->parent;
+
+       if (p->phy)
+               phy_stop(p->phy);
 
        dev_mc_unsync(master, dev);
        dev_uc_unsync(master, dev);
@@ -111,6 +129,9 @@ static int dsa_slave_close(struct net_device *dev)
        if (!ether_addr_equal(dev->dev_addr, master->dev_addr))
                dev_uc_del(master, dev->dev_addr);
 
+       if (ds->drv->port_disable)
+               ds->drv->port_disable(ds, p->port, p->phy);
+
        return 0;
 }
 
@@ -301,6 +322,65 @@ static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
        return -EOPNOTSUPP;
 }
 
+static void dsa_slave_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+
+       if (ds->drv->get_wol)
+               ds->drv->get_wol(ds, p->port, w);
+}
+
+static int dsa_slave_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+       int ret = -EOPNOTSUPP;
+
+       if (ds->drv->set_wol)
+               ret = ds->drv->set_wol(ds, p->port, w);
+
+       return ret;
+}
+
+static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+       int ret;
+
+       if (!ds->drv->set_eee)
+               return -EOPNOTSUPP;
+
+       ret = ds->drv->set_eee(ds, p->port, p->phy, e);
+       if (ret)
+               return ret;
+
+       if (p->phy)
+               ret = phy_ethtool_set_eee(p->phy, e);
+
+       return ret;
+}
+
+static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       struct dsa_switch *ds = p->parent;
+       int ret;
+
+       if (!ds->drv->get_eee)
+               return -EOPNOTSUPP;
+
+       ret = ds->drv->get_eee(ds, p->port, e);
+       if (ret)
+               return ret;
+
+       if (p->phy)
+               ret = phy_ethtool_get_eee(p->phy, e);
+
+       return ret;
+}
+
 static const struct ethtool_ops dsa_slave_ethtool_ops = {
        .get_settings           = dsa_slave_get_settings,
        .set_settings           = dsa_slave_set_settings,
@@ -310,6 +390,10 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
        .get_strings            = dsa_slave_get_strings,
        .get_ethtool_stats      = dsa_slave_get_ethtool_stats,
        .get_sset_count         = dsa_slave_get_sset_count,
+       .set_wol                = dsa_slave_set_wol,
+       .get_wol                = dsa_slave_get_wol,
+       .set_eee                = dsa_slave_set_eee,
+       .get_eee                = dsa_slave_get_eee,
 };
 
 static const struct net_device_ops dsa_slave_netdev_ops = {
@@ -371,6 +455,7 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
        struct dsa_chip_data *cd = ds->pd;
        struct device_node *phy_dn, *port_dn;
        bool phy_is_fixed = false;
+       u32 phy_flags = 0;
        int ret;
 
        port_dn = cd->port_dn[p->port];
@@ -390,9 +475,12 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
                phy_dn = port_dn;
        }
 
+       if (ds->drv->get_phy_flags)
+               phy_flags = ds->drv->get_phy_flags(ds, p->port);
+
        if (phy_dn)
                p->phy = of_phy_connect(slave_dev, phy_dn,
-                                       dsa_slave_adjust_link, 0,
+                                       dsa_slave_adjust_link, phy_flags,
                                        p->phy_interface);
 
        if (p->phy && phy_is_fixed)
@@ -408,6 +496,37 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
                        p->phy->addr, p->phy->drv->name);
 }
 
+int dsa_slave_suspend(struct net_device *slave_dev)
+{
+       struct dsa_slave_priv *p = netdev_priv(slave_dev);
+
+       netif_device_detach(slave_dev);
+
+       if (p->phy) {
+               phy_stop(p->phy);
+               p->old_pause = -1;
+               p->old_link = -1;
+               p->old_duplex = -1;
+               phy_suspend(p->phy);
+       }
+
+       return 0;
+}
+
+int dsa_slave_resume(struct net_device *slave_dev)
+{
+       struct dsa_slave_priv *p = netdev_priv(slave_dev);
+
+       netif_device_attach(slave_dev);
+
+       if (p->phy) {
+               phy_resume(p->phy);
+               phy_start(p->phy);
+       }
+
+       return 0;
+}
+
 struct net_device *
 dsa_slave_create(struct dsa_switch *ds, struct device *parent,
                 int port, char *name)
@@ -480,6 +599,9 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
        netif_carrier_off(slave_dev);
 
        if (p->phy != NULL) {
+               if (ds->drv->get_phy_flags(ds, port))
+                       p->phy->dev_flags |= ds->drv->get_phy_flags(ds, port);
+
                phy_attach(slave_dev, dev_name(&p->phy->dev),
                           PHY_INTERFACE_MODE_GMII);
 
@@ -487,7 +609,6 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
                p->phy->speed = 0;
                p->phy->duplex = 0;
                p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
-               phy_start_aneg(p->phy);
        }
 
        return slave_dev;