Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / dsa / dsa.c
index 0a49632fac478f1ac17b3f0159cbdf748fe25110..6905f2d84c44f48ba8611b222910a3b06a6464f6 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/list.h>
-#include <linux/netdevice.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/module.h>
@@ -44,7 +43,7 @@ void unregister_switch_driver(struct dsa_switch_driver *drv)
 EXPORT_SYMBOL_GPL(unregister_switch_driver);
 
 static struct dsa_switch_driver *
-dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name)
+dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name)
 {
        struct dsa_switch_driver *ret;
        struct list_head *list;
@@ -59,7 +58,7 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name)
 
                drv = list_entry(list, struct dsa_switch_driver, list);
 
-               name = drv->probe(bus, sw_addr);
+               name = drv->probe(host_dev, sw_addr);
                if (name != NULL) {
                        ret = drv;
                        break;
@@ -76,7 +75,7 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name)
 /* basic switch operations **************************************************/
 static struct dsa_switch *
 dsa_switch_setup(struct dsa_switch_tree *dst, int index,
-                struct device *parent, struct mii_bus *bus)
+                struct device *parent, struct device *host_dev)
 {
        struct dsa_chip_data *pd = dst->pd->chip + index;
        struct dsa_switch_driver *drv;
@@ -89,7 +88,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
        /*
         * Probe for switch model.
         */
-       drv = dsa_switch_probe(bus, pd->sw_addr, &name);
+       drv = dsa_switch_probe(host_dev, pd->sw_addr, &name);
        if (drv == NULL) {
                printk(KERN_ERR "%s[%d]: could not detect attached switch\n",
                       dst->master_netdev->name, index);
@@ -110,8 +109,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
        ds->index = index;
        ds->pd = dst->pd->chip + index;
        ds->drv = drv;
-       ds->master_mii_bus = bus;
-
+       ds->master_dev = host_dev;
 
        /*
         * Validate supplied switch configuration.
@@ -144,14 +142,44 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
                goto out;
        }
 
+       /* Make the built-in MII bus mask match the number of ports,
+        * switch drivers can override this later
+        */
+       ds->phys_mii_mask = ds->phys_port_mask;
+
        /*
         * If the CPU connects to this switch, set the switch tree
         * tagging protocol to the preferred tagging format of this
         * switch.
         */
-       if (ds->dst->cpu_switch == index)
-               ds->dst->tag_protocol = drv->tag_protocol;
+       if (dst->cpu_switch == index) {
+               switch (drv->tag_protocol) {
+#ifdef CONFIG_NET_DSA_TAG_DSA
+               case DSA_TAG_PROTO_DSA:
+                       dst->rcv = dsa_netdev_ops.rcv;
+                       break;
+#endif
+#ifdef CONFIG_NET_DSA_TAG_EDSA
+               case DSA_TAG_PROTO_EDSA:
+                       dst->rcv = edsa_netdev_ops.rcv;
+                       break;
+#endif
+#ifdef CONFIG_NET_DSA_TAG_TRAILER
+               case DSA_TAG_PROTO_TRAILER:
+                       dst->rcv = trailer_netdev_ops.rcv;
+                       break;
+#endif
+#ifdef CONFIG_NET_DSA_TAG_BRCM
+               case DSA_TAG_PROTO_BRCM:
+                       dst->rcv = brcm_netdev_ops.rcv;
+                       break;
+#endif
+               default:
+                       break;
+               }
 
+               dst->tag_protocol = drv->tag_protocol;
+       }
 
        /*
         * Do basic register setup.
@@ -210,6 +238,49 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
 {
 }
 
+static int dsa_switch_suspend(struct dsa_switch *ds)
+{
+       int i, ret = 0;
+
+       /* Suspend slave network devices */
+       for (i = 0; i < DSA_MAX_PORTS; i++) {
+               if (!(ds->phys_port_mask & (1 << i)))
+                       continue;
+
+               ret = dsa_slave_suspend(ds->ports[i]);
+               if (ret)
+                       return ret;
+       }
+
+       if (ds->drv->suspend)
+               ret = ds->drv->suspend(ds);
+
+       return ret;
+}
+
+static int dsa_switch_resume(struct dsa_switch *ds)
+{
+       int i, ret = 0;
+
+       if (ds->drv->resume)
+               ret = ds->drv->resume(ds);
+
+       if (ret)
+               return ret;
+
+       /* Resume slave network devices */
+       for (i = 0; i < DSA_MAX_PORTS; i++) {
+               if (!(ds->phys_port_mask & (1 << i)))
+                       continue;
+
+               ret = dsa_slave_resume(ds->ports[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 
 /* link polling *************************************************************/
 static void dsa_link_poll_work(struct work_struct *ugly)
@@ -256,7 +327,7 @@ static struct device *dev_find_class(struct device *parent, char *class)
        return device_find_child(parent, class, dev_is_class);
 }
 
-static struct mii_bus *dev_to_mii_bus(struct device *dev)
+struct mii_bus *dsa_host_dev_to_mii_bus(struct device *dev)
 {
        struct device *d;
 
@@ -272,6 +343,7 @@ static struct mii_bus *dev_to_mii_bus(struct device *dev)
 
        return NULL;
 }
+EXPORT_SYMBOL_GPL(dsa_host_dev_to_mii_bus);
 
 static struct net_device *dev_to_net_device(struct device *dev)
 {
@@ -410,7 +482,8 @@ static int dsa_of_probe(struct platform_device *pdev)
                chip_index++;
                cd = &pd->chip[chip_index];
 
-               cd->mii_bus = &mdio_bus->dev;
+               cd->of_node = child;
+               cd->host_dev = &mdio_bus->dev;
 
                sw_addr = of_get_property(child, "reg", NULL);
                if (!sw_addr)
@@ -431,6 +504,8 @@ static int dsa_of_probe(struct platform_device *pdev)
                        if (!port_name)
                                continue;
 
+                       cd->port_dn[port_index] = port;
+
                        cd->port_names[port_index] = kstrdup(port_name,
                                        GFP_KERNEL);
                        if (!cd->port_names[port_index]) {
@@ -534,17 +609,9 @@ static int dsa_probe(struct platform_device *pdev)
        dst->cpu_port = -1;
 
        for (i = 0; i < pd->nr_chips; i++) {
-               struct mii_bus *bus;
                struct dsa_switch *ds;
 
-               bus = dev_to_mii_bus(pd->chip[i].mii_bus);
-               if (bus == NULL) {
-                       printk(KERN_ERR "%s[%d]: no mii bus found for "
-                               "dsa switch\n", dev->name, i);
-                       continue;
-               }
-
-               ds = dsa_switch_setup(dst, i, &pdev->dev, bus);
+               ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev);
                if (IS_ERR(ds)) {
                        printk(KERN_ERR "%s[%d]: couldn't create dsa switch "
                                "instance (error %ld)\n", dev->name, i,
@@ -608,7 +675,62 @@ static void dsa_shutdown(struct platform_device *pdev)
 {
 }
 
+static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
+                         struct packet_type *pt, struct net_device *orig_dev)
+{
+       struct dsa_switch_tree *dst = dev->dsa_ptr;
+
+       if (unlikely(dst == NULL)) {
+               kfree_skb(skb);
+               return 0;
+       }
+
+       return dst->rcv(skb, dev, pt, orig_dev);
+}
+
+static struct packet_type dsa_pack_type __read_mostly = {
+       .type   = cpu_to_be16(ETH_P_XDSA),
+       .func   = dsa_switch_rcv,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int dsa_suspend(struct device *d)
+{
+       struct platform_device *pdev = to_platform_device(d);
+       struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
+       int i, ret = 0;
+
+       for (i = 0; i < dst->pd->nr_chips; i++) {
+               struct dsa_switch *ds = dst->ds[i];
+
+               if (ds != NULL)
+                       ret = dsa_switch_suspend(ds);
+       }
+
+       return ret;
+}
+
+static int dsa_resume(struct device *d)
+{
+       struct platform_device *pdev = to_platform_device(d);
+       struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
+       int i, ret = 0;
+
+       for (i = 0; i < dst->pd->nr_chips; i++) {
+               struct dsa_switch *ds = dst->ds[i];
+
+               if (ds != NULL)
+                       ret = dsa_switch_resume(ds);
+       }
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume);
+
 static const struct of_device_id dsa_of_match_table[] = {
+       { .compatible = "brcm,bcm7445-switch-v4.0" },
        { .compatible = "marvell,dsa", },
        {}
 };
@@ -622,6 +744,7 @@ static struct platform_driver dsa_driver = {
                .name   = "dsa",
                .owner  = THIS_MODULE,
                .of_match_table = dsa_of_match_table,
+               .pm     = &dsa_pm_ops,
        },
 };
 
@@ -633,30 +756,15 @@ static int __init dsa_init_module(void)
        if (rc)
                return rc;
 
-#ifdef CONFIG_NET_DSA_TAG_DSA
-       dev_add_pack(&dsa_packet_type);
-#endif
-#ifdef CONFIG_NET_DSA_TAG_EDSA
-       dev_add_pack(&edsa_packet_type);
-#endif
-#ifdef CONFIG_NET_DSA_TAG_TRAILER
-       dev_add_pack(&trailer_packet_type);
-#endif
+       dev_add_pack(&dsa_pack_type);
+
        return 0;
 }
 module_init(dsa_init_module);
 
 static void __exit dsa_cleanup_module(void)
 {
-#ifdef CONFIG_NET_DSA_TAG_TRAILER
-       dev_remove_pack(&trailer_packet_type);
-#endif
-#ifdef CONFIG_NET_DSA_TAG_EDSA
-       dev_remove_pack(&edsa_packet_type);
-#endif
-#ifdef CONFIG_NET_DSA_TAG_DSA
-       dev_remove_pack(&dsa_packet_type);
-#endif
+       dev_remove_pack(&dsa_pack_type);
        platform_driver_unregister(&dsa_driver);
 }
 module_exit(dsa_cleanup_module);