Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
[firefly-linux-kernel-4.4.55.git] / net / dsa / slave.c
index 809eeb13eb123e78bec5d6aa61cfc87d8074d640..36953c84ff2d44e488ba79fd3125cf7df2abdda6 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;
 }
 
 
@@ -63,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))
@@ -85,8 +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);
@@ -101,6 +113,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);
@@ -112,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;
 }
 
@@ -176,9 +195,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 +321,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,
@@ -312,6 +389,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 = {
@@ -325,11 +406,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 +454,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 +474,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 +495,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,41 +546,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 DSA_TAG_PROTO_DSA:
-               ds->dst->ops = &dsa_netdev_ops;
+               p->xmit = dsa_netdev_ops.xmit;
                break;
 #endif
 #ifdef CONFIG_NET_DSA_TAG_EDSA
        case DSA_TAG_PROTO_EDSA:
-               ds->dst->ops = &edsa_netdev_ops;
+               p->xmit = edsa_netdev_ops.xmit;
                break;
 #endif
 #ifdef CONFIG_NET_DSA_TAG_TRAILER
        case DSA_TAG_PROTO_TRAILER:
-               ds->dst->ops = &trailer_netdev_ops;
+               p->xmit = trailer_netdev_ops.xmit;
                break;
 #endif
 #ifdef CONFIG_NET_DSA_TAG_BRCM
        case DSA_TAG_PROTO_BRCM:
-               ds->dst->ops = &brcm_netdev_ops;
+               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;
@@ -487,6 +598,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);