net: dsa: allow enabling and disable switch ports
authorFlorian Fainelli <f.fainelli@gmail.com>
Thu, 25 Sep 2014 00:05:18 +0000 (17:05 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 28 Sep 2014 21:14:08 +0000 (17:14 -0400)
Whenever a per-port network device is used/unused, invoke the switch
driver port_enable/port_disable callbacks to allow saving as much power
as possible by disabling unused parts of the switch (RX/TX logic, memory
arrays, PHYs...). We supply a PHY device argument to make sure the
switch driver can act on the PHY device if needed (like putting/taking
the PHY out of deep low power mode).

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/dsa.h
net/dsa/slave.c

index d8054fb4a4dffaf45d3864b938d99ffb6c32b6bd..4f664fe0e42ca631e6975542a3f8dc258f08b397 100644 (file)
@@ -224,6 +224,14 @@ struct dsa_switch_driver {
         */
        int     (*suspend)(struct dsa_switch *ds);
        int     (*resume)(struct dsa_switch *ds);
+
+       /*
+        * Port enable/disable
+        */
+       int     (*port_enable)(struct dsa_switch *ds, int port,
+                              struct phy_device *phy);
+       void    (*port_disable)(struct dsa_switch *ds, int port,
+                               struct phy_device *phy);
 };
 
 void register_switch_driver(struct dsa_switch_driver *type);
index 4392e983abda87d1d9eea3df8343c64b533f6cfe..182d30ae6818cfc392ab781aaa00011f1732d761 100644 (file)
@@ -62,6 +62,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,11 +85,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);
@@ -103,6 +113,7 @@ 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);
@@ -117,6 +128,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;
 }