Merge branch 'dsa-suspend'
[firefly-linux-kernel-4.4.55.git] / net / dsa / slave.c
index 18ff53836fe3a10a4ae7aca9ad68121c90cbccd0..43c1e4ade6892356fbc13ff5c6fefec4ea776f21 100644 (file)
@@ -9,7 +9,6 @@
  */
 
 #include <linux/list.h>
-#include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/phy.h>
 #include <linux/of_net.h>
@@ -45,7 +44,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;
 }
 
 
@@ -176,9 +175,8 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
-       struct dsa_switch_tree *dst = p->parent->dst;
 
-       return dst->ops->xmit(skb, dev);
+       return p->xmit(skb, dev);
 }
 
 static netdev_tx_t dsa_slave_notag_xmit(struct sk_buff *skb,
@@ -303,6 +301,27 @@ 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 const struct ethtool_ops dsa_slave_ethtool_ops = {
        .get_settings           = dsa_slave_get_settings,
        .set_settings           = dsa_slave_set_settings,
@@ -312,6 +331,8 @@ 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,
 };
 
 static const struct net_device_ops dsa_slave_netdev_ops = {
@@ -325,11 +346,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
        .ndo_do_ioctl           = dsa_slave_ioctl,
 };
 
-static const struct dsa_device_ops notag_netdev_ops = {
-       .xmit   = dsa_slave_notag_xmit,
-       .rcv    = NULL,
-};
-
 static void dsa_slave_adjust_link(struct net_device *dev)
 {
        struct dsa_slave_priv *p = netdev_priv(dev);
@@ -378,6 +394,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];
@@ -397,9 +414,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)
@@ -415,6 +435,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)
@@ -435,36 +486,41 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
        slave_dev->tx_queue_len = 0;
        slave_dev->netdev_ops = &dsa_slave_netdev_ops;
 
+       SET_NETDEV_DEV(slave_dev, parent);
+       slave_dev->dev.of_node = ds->pd->port_dn[port];
+       slave_dev->vlan_features = master->vlan_features;
+
+       p = netdev_priv(slave_dev);
+       p->dev = slave_dev;
+       p->parent = ds;
+       p->port = port;
+
        switch (ds->dst->tag_protocol) {
 #ifdef CONFIG_NET_DSA_TAG_DSA
-       case htons(ETH_P_DSA):
-               ds->dst->ops = &dsa_netdev_ops;
+       case DSA_TAG_PROTO_DSA:
+               p->xmit = dsa_netdev_ops.xmit;
                break;
 #endif
 #ifdef CONFIG_NET_DSA_TAG_EDSA
-       case htons(ETH_P_EDSA):
-               ds->dst->ops = &edsa_netdev_ops;
+       case DSA_TAG_PROTO_EDSA:
+               p->xmit = edsa_netdev_ops.xmit;
                break;
 #endif
 #ifdef CONFIG_NET_DSA_TAG_TRAILER
-       case htons(ETH_P_TRAILER):
-               ds->dst->ops = &trailer_netdev_ops;
+       case DSA_TAG_PROTO_TRAILER:
+               p->xmit = trailer_netdev_ops.xmit;
+               break;
+#endif
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+       case DSA_TAG_PROTO_BRCM:
+               p->xmit = brcm_netdev_ops.xmit;
                break;
 #endif
        default:
-               ds->dst->ops = &notag_netdev_ops;
+               p->xmit = dsa_slave_notag_xmit;
                break;
        }
 
-       SET_NETDEV_DEV(slave_dev, parent);
-       slave_dev->dev.of_node = ds->pd->port_dn[port];
-       slave_dev->vlan_features = master->vlan_features;
-
-       p = netdev_priv(slave_dev);
-       p->dev = slave_dev;
-       p->parent = ds;
-       p->port = port;
-
        p->old_pause = -1;
        p->old_link = -1;
        p->old_duplex = -1;
@@ -482,6 +538,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);