diff options
Diffstat (limited to 'net/dsa/dsa.c')
-rw-r--r-- | net/dsa/dsa.c | 251 |
1 files changed, 144 insertions, 107 deletions
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 4dea2e0681d1..5eaadabe23a1 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_mdio.h> | 21 | #include <linux/of_mdio.h> |
22 | #include <linux/of_platform.h> | 22 | #include <linux/of_platform.h> |
23 | #include <linux/of_net.h> | ||
23 | #include <linux/sysfs.h> | 24 | #include <linux/sysfs.h> |
24 | #include "dsa_priv.h" | 25 | #include "dsa_priv.h" |
25 | 26 | ||
@@ -175,43 +176,14 @@ __ATTRIBUTE_GROUPS(dsa_hwmon); | |||
175 | #endif /* CONFIG_NET_DSA_HWMON */ | 176 | #endif /* CONFIG_NET_DSA_HWMON */ |
176 | 177 | ||
177 | /* basic switch operations **************************************************/ | 178 | /* basic switch operations **************************************************/ |
178 | static struct dsa_switch * | 179 | static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) |
179 | dsa_switch_setup(struct dsa_switch_tree *dst, int index, | ||
180 | struct device *parent, struct device *host_dev) | ||
181 | { | 180 | { |
182 | struct dsa_chip_data *pd = dst->pd->chip + index; | 181 | struct dsa_switch_driver *drv = ds->drv; |
183 | struct dsa_switch_driver *drv; | 182 | struct dsa_switch_tree *dst = ds->dst; |
184 | struct dsa_switch *ds; | 183 | struct dsa_chip_data *pd = ds->pd; |
185 | int ret; | ||
186 | char *name; | ||
187 | int i; | ||
188 | bool valid_name_found = false; | 184 | bool valid_name_found = false; |
189 | 185 | int index = ds->index; | |
190 | /* | 186 | int i, ret; |
191 | * Probe for switch model. | ||
192 | */ | ||
193 | drv = dsa_switch_probe(host_dev, pd->sw_addr, &name); | ||
194 | if (drv == NULL) { | ||
195 | netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n", | ||
196 | index); | ||
197 | return ERR_PTR(-EINVAL); | ||
198 | } | ||
199 | netdev_info(dst->master_netdev, "[%d]: detected a %s switch\n", | ||
200 | index, name); | ||
201 | |||
202 | |||
203 | /* | ||
204 | * Allocate and initialise switch state. | ||
205 | */ | ||
206 | ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL); | ||
207 | if (ds == NULL) | ||
208 | return ERR_PTR(-ENOMEM); | ||
209 | |||
210 | ds->dst = dst; | ||
211 | ds->index = index; | ||
212 | ds->pd = dst->pd->chip + index; | ||
213 | ds->drv = drv; | ||
214 | ds->master_dev = host_dev; | ||
215 | 187 | ||
216 | /* | 188 | /* |
217 | * Validate supplied switch configuration. | 189 | * Validate supplied switch configuration. |
@@ -256,7 +228,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
256 | * switch. | 228 | * switch. |
257 | */ | 229 | */ |
258 | if (dst->cpu_switch == index) { | 230 | if (dst->cpu_switch == index) { |
259 | switch (drv->tag_protocol) { | 231 | switch (ds->tag_protocol) { |
260 | #ifdef CONFIG_NET_DSA_TAG_DSA | 232 | #ifdef CONFIG_NET_DSA_TAG_DSA |
261 | case DSA_TAG_PROTO_DSA: | 233 | case DSA_TAG_PROTO_DSA: |
262 | dst->rcv = dsa_netdev_ops.rcv; | 234 | dst->rcv = dsa_netdev_ops.rcv; |
@@ -284,7 +256,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
284 | goto out; | 256 | goto out; |
285 | } | 257 | } |
286 | 258 | ||
287 | dst->tag_protocol = drv->tag_protocol; | 259 | dst->tag_protocol = ds->tag_protocol; |
288 | } | 260 | } |
289 | 261 | ||
290 | /* | 262 | /* |
@@ -314,19 +286,15 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
314 | * Create network devices for physical switch ports. | 286 | * Create network devices for physical switch ports. |
315 | */ | 287 | */ |
316 | for (i = 0; i < DSA_MAX_PORTS; i++) { | 288 | for (i = 0; i < DSA_MAX_PORTS; i++) { |
317 | struct net_device *slave_dev; | ||
318 | |||
319 | if (!(ds->phys_port_mask & (1 << i))) | 289 | if (!(ds->phys_port_mask & (1 << i))) |
320 | continue; | 290 | continue; |
321 | 291 | ||
322 | slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]); | 292 | ret = dsa_slave_create(ds, parent, i, pd->port_names[i]); |
323 | if (slave_dev == NULL) { | 293 | if (ret < 0) { |
324 | netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n", | 294 | netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n", |
325 | index, i, pd->port_names[i]); | 295 | index, i, pd->port_names[i]); |
326 | continue; | 296 | ret = 0; |
327 | } | 297 | } |
328 | |||
329 | ds->ports[i] = slave_dev; | ||
330 | } | 298 | } |
331 | 299 | ||
332 | #ifdef CONFIG_NET_DSA_HWMON | 300 | #ifdef CONFIG_NET_DSA_HWMON |
@@ -354,13 +322,57 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
354 | } | 322 | } |
355 | #endif /* CONFIG_NET_DSA_HWMON */ | 323 | #endif /* CONFIG_NET_DSA_HWMON */ |
356 | 324 | ||
357 | return ds; | 325 | return ret; |
358 | 326 | ||
359 | out_free: | 327 | out_free: |
360 | mdiobus_free(ds->slave_mii_bus); | 328 | mdiobus_free(ds->slave_mii_bus); |
361 | out: | 329 | out: |
362 | kfree(ds); | 330 | kfree(ds); |
363 | return ERR_PTR(ret); | 331 | return ret; |
332 | } | ||
333 | |||
334 | static struct dsa_switch * | ||
335 | dsa_switch_setup(struct dsa_switch_tree *dst, int index, | ||
336 | struct device *parent, struct device *host_dev) | ||
337 | { | ||
338 | struct dsa_chip_data *pd = dst->pd->chip + index; | ||
339 | struct dsa_switch_driver *drv; | ||
340 | struct dsa_switch *ds; | ||
341 | int ret; | ||
342 | char *name; | ||
343 | |||
344 | /* | ||
345 | * Probe for switch model. | ||
346 | */ | ||
347 | drv = dsa_switch_probe(host_dev, pd->sw_addr, &name); | ||
348 | if (drv == NULL) { | ||
349 | netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n", | ||
350 | index); | ||
351 | return ERR_PTR(-EINVAL); | ||
352 | } | ||
353 | netdev_info(dst->master_netdev, "[%d]: detected a %s switch\n", | ||
354 | index, name); | ||
355 | |||
356 | |||
357 | /* | ||
358 | * Allocate and initialise switch state. | ||
359 | */ | ||
360 | ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL); | ||
361 | if (ds == NULL) | ||
362 | return NULL; | ||
363 | |||
364 | ds->dst = dst; | ||
365 | ds->index = index; | ||
366 | ds->pd = pd; | ||
367 | ds->drv = drv; | ||
368 | ds->tag_protocol = drv->tag_protocol; | ||
369 | ds->master_dev = host_dev; | ||
370 | |||
371 | ret = dsa_switch_setup_one(ds, parent); | ||
372 | if (ret) | ||
373 | return NULL; | ||
374 | |||
375 | return ds; | ||
364 | } | 376 | } |
365 | 377 | ||
366 | static void dsa_switch_destroy(struct dsa_switch *ds) | 378 | static void dsa_switch_destroy(struct dsa_switch *ds) |
@@ -378,7 +390,7 @@ static int dsa_switch_suspend(struct dsa_switch *ds) | |||
378 | 390 | ||
379 | /* Suspend slave network devices */ | 391 | /* Suspend slave network devices */ |
380 | for (i = 0; i < DSA_MAX_PORTS; i++) { | 392 | for (i = 0; i < DSA_MAX_PORTS; i++) { |
381 | if (!(ds->phys_port_mask & (1 << i))) | 393 | if (!dsa_is_port_initialized(ds, i)) |
382 | continue; | 394 | continue; |
383 | 395 | ||
384 | ret = dsa_slave_suspend(ds->ports[i]); | 396 | ret = dsa_slave_suspend(ds->ports[i]); |
@@ -404,7 +416,7 @@ static int dsa_switch_resume(struct dsa_switch *ds) | |||
404 | 416 | ||
405 | /* Resume slave network devices */ | 417 | /* Resume slave network devices */ |
406 | for (i = 0; i < DSA_MAX_PORTS; i++) { | 418 | for (i = 0; i < DSA_MAX_PORTS; i++) { |
407 | if (!(ds->phys_port_mask & (1 << i))) | 419 | if (!dsa_is_port_initialized(ds, i)) |
408 | continue; | 420 | continue; |
409 | 421 | ||
410 | ret = dsa_slave_resume(ds->ports[i]); | 422 | ret = dsa_slave_resume(ds->ports[i]); |
@@ -558,12 +570,12 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd) | |||
558 | kfree(pd->chip); | 570 | kfree(pd->chip); |
559 | } | 571 | } |
560 | 572 | ||
561 | static int dsa_of_probe(struct platform_device *pdev) | 573 | static int dsa_of_probe(struct device *dev) |
562 | { | 574 | { |
563 | struct device_node *np = pdev->dev.of_node; | 575 | struct device_node *np = dev->of_node; |
564 | struct device_node *child, *mdio, *ethernet, *port, *link; | 576 | struct device_node *child, *mdio, *ethernet, *port, *link; |
565 | struct mii_bus *mdio_bus; | 577 | struct mii_bus *mdio_bus; |
566 | struct platform_device *ethernet_dev; | 578 | struct net_device *ethernet_dev; |
567 | struct dsa_platform_data *pd; | 579 | struct dsa_platform_data *pd; |
568 | struct dsa_chip_data *cd; | 580 | struct dsa_chip_data *cd; |
569 | const char *port_name; | 581 | const char *port_name; |
@@ -578,22 +590,22 @@ static int dsa_of_probe(struct platform_device *pdev) | |||
578 | 590 | ||
579 | mdio_bus = of_mdio_find_bus(mdio); | 591 | mdio_bus = of_mdio_find_bus(mdio); |
580 | if (!mdio_bus) | 592 | if (!mdio_bus) |
581 | return -EINVAL; | 593 | return -EPROBE_DEFER; |
582 | 594 | ||
583 | ethernet = of_parse_phandle(np, "dsa,ethernet", 0); | 595 | ethernet = of_parse_phandle(np, "dsa,ethernet", 0); |
584 | if (!ethernet) | 596 | if (!ethernet) |
585 | return -EINVAL; | 597 | return -EINVAL; |
586 | 598 | ||
587 | ethernet_dev = of_find_device_by_node(ethernet); | 599 | ethernet_dev = of_find_net_device_by_node(ethernet); |
588 | if (!ethernet_dev) | 600 | if (!ethernet_dev) |
589 | return -ENODEV; | 601 | return -EPROBE_DEFER; |
590 | 602 | ||
591 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | 603 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); |
592 | if (!pd) | 604 | if (!pd) |
593 | return -ENOMEM; | 605 | return -ENOMEM; |
594 | 606 | ||
595 | pdev->dev.platform_data = pd; | 607 | dev->platform_data = pd; |
596 | pd->netdev = ðernet_dev->dev; | 608 | pd->of_netdev = ethernet_dev; |
597 | pd->nr_chips = of_get_available_child_count(np); | 609 | pd->nr_chips = of_get_available_child_count(np); |
598 | if (pd->nr_chips > DSA_MAX_SWITCHES) | 610 | if (pd->nr_chips > DSA_MAX_SWITCHES) |
599 | pd->nr_chips = DSA_MAX_SWITCHES; | 611 | pd->nr_chips = DSA_MAX_SWITCHES; |
@@ -665,72 +677,35 @@ out_free_chip: | |||
665 | dsa_of_free_platform_data(pd); | 677 | dsa_of_free_platform_data(pd); |
666 | out_free: | 678 | out_free: |
667 | kfree(pd); | 679 | kfree(pd); |
668 | pdev->dev.platform_data = NULL; | 680 | dev->platform_data = NULL; |
669 | return ret; | 681 | return ret; |
670 | } | 682 | } |
671 | 683 | ||
672 | static void dsa_of_remove(struct platform_device *pdev) | 684 | static void dsa_of_remove(struct device *dev) |
673 | { | 685 | { |
674 | struct dsa_platform_data *pd = pdev->dev.platform_data; | 686 | struct dsa_platform_data *pd = dev->platform_data; |
675 | 687 | ||
676 | if (!pdev->dev.of_node) | 688 | if (!dev->of_node) |
677 | return; | 689 | return; |
678 | 690 | ||
679 | dsa_of_free_platform_data(pd); | 691 | dsa_of_free_platform_data(pd); |
680 | kfree(pd); | 692 | kfree(pd); |
681 | } | 693 | } |
682 | #else | 694 | #else |
683 | static inline int dsa_of_probe(struct platform_device *pdev) | 695 | static inline int dsa_of_probe(struct device *dev) |
684 | { | 696 | { |
685 | return 0; | 697 | return 0; |
686 | } | 698 | } |
687 | 699 | ||
688 | static inline void dsa_of_remove(struct platform_device *pdev) | 700 | static inline void dsa_of_remove(struct device *dev) |
689 | { | 701 | { |
690 | } | 702 | } |
691 | #endif | 703 | #endif |
692 | 704 | ||
693 | static int dsa_probe(struct platform_device *pdev) | 705 | static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, |
706 | struct device *parent, struct dsa_platform_data *pd) | ||
694 | { | 707 | { |
695 | struct dsa_platform_data *pd = pdev->dev.platform_data; | 708 | int i; |
696 | struct net_device *dev; | ||
697 | struct dsa_switch_tree *dst; | ||
698 | int i, ret; | ||
699 | |||
700 | pr_notice_once("Distributed Switch Architecture driver version %s\n", | ||
701 | dsa_driver_version); | ||
702 | |||
703 | if (pdev->dev.of_node) { | ||
704 | ret = dsa_of_probe(pdev); | ||
705 | if (ret) | ||
706 | return ret; | ||
707 | |||
708 | pd = pdev->dev.platform_data; | ||
709 | } | ||
710 | |||
711 | if (pd == NULL || pd->netdev == NULL) | ||
712 | return -EINVAL; | ||
713 | |||
714 | dev = dev_to_net_device(pd->netdev); | ||
715 | if (dev == NULL) { | ||
716 | ret = -EINVAL; | ||
717 | goto out; | ||
718 | } | ||
719 | |||
720 | if (dev->dsa_ptr != NULL) { | ||
721 | dev_put(dev); | ||
722 | ret = -EEXIST; | ||
723 | goto out; | ||
724 | } | ||
725 | |||
726 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | ||
727 | if (dst == NULL) { | ||
728 | dev_put(dev); | ||
729 | ret = -ENOMEM; | ||
730 | goto out; | ||
731 | } | ||
732 | |||
733 | platform_set_drvdata(pdev, dst); | ||
734 | 709 | ||
735 | dst->pd = pd; | 710 | dst->pd = pd; |
736 | dst->master_netdev = dev; | 711 | dst->master_netdev = dev; |
@@ -740,7 +715,7 @@ static int dsa_probe(struct platform_device *pdev) | |||
740 | for (i = 0; i < pd->nr_chips; i++) { | 715 | for (i = 0; i < pd->nr_chips; i++) { |
741 | struct dsa_switch *ds; | 716 | struct dsa_switch *ds; |
742 | 717 | ||
743 | ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev); | 718 | ds = dsa_switch_setup(dst, i, parent, pd->chip[i].host_dev); |
744 | if (IS_ERR(ds)) { | 719 | if (IS_ERR(ds)) { |
745 | netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n", | 720 | netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n", |
746 | i, PTR_ERR(ds)); | 721 | i, PTR_ERR(ds)); |
@@ -768,18 +743,67 @@ static int dsa_probe(struct platform_device *pdev) | |||
768 | dst->link_poll_timer.expires = round_jiffies(jiffies + HZ); | 743 | dst->link_poll_timer.expires = round_jiffies(jiffies + HZ); |
769 | add_timer(&dst->link_poll_timer); | 744 | add_timer(&dst->link_poll_timer); |
770 | } | 745 | } |
746 | } | ||
747 | |||
748 | static int dsa_probe(struct platform_device *pdev) | ||
749 | { | ||
750 | struct dsa_platform_data *pd = pdev->dev.platform_data; | ||
751 | struct net_device *dev; | ||
752 | struct dsa_switch_tree *dst; | ||
753 | int ret; | ||
754 | |||
755 | pr_notice_once("Distributed Switch Architecture driver version %s\n", | ||
756 | dsa_driver_version); | ||
757 | |||
758 | if (pdev->dev.of_node) { | ||
759 | ret = dsa_of_probe(&pdev->dev); | ||
760 | if (ret) | ||
761 | return ret; | ||
762 | |||
763 | pd = pdev->dev.platform_data; | ||
764 | } | ||
765 | |||
766 | if (pd == NULL || (pd->netdev == NULL && pd->of_netdev == NULL)) | ||
767 | return -EINVAL; | ||
768 | |||
769 | if (pd->of_netdev) { | ||
770 | dev = pd->of_netdev; | ||
771 | dev_hold(dev); | ||
772 | } else { | ||
773 | dev = dev_to_net_device(pd->netdev); | ||
774 | } | ||
775 | if (dev == NULL) { | ||
776 | ret = -EPROBE_DEFER; | ||
777 | goto out; | ||
778 | } | ||
779 | |||
780 | if (dev->dsa_ptr != NULL) { | ||
781 | dev_put(dev); | ||
782 | ret = -EEXIST; | ||
783 | goto out; | ||
784 | } | ||
785 | |||
786 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | ||
787 | if (dst == NULL) { | ||
788 | dev_put(dev); | ||
789 | ret = -ENOMEM; | ||
790 | goto out; | ||
791 | } | ||
792 | |||
793 | platform_set_drvdata(pdev, dst); | ||
794 | |||
795 | dsa_setup_dst(dst, dev, &pdev->dev, pd); | ||
771 | 796 | ||
772 | return 0; | 797 | return 0; |
773 | 798 | ||
774 | out: | 799 | out: |
775 | dsa_of_remove(pdev); | 800 | dsa_of_remove(&pdev->dev); |
776 | 801 | ||
777 | return ret; | 802 | return ret; |
778 | } | 803 | } |
779 | 804 | ||
780 | static int dsa_remove(struct platform_device *pdev) | 805 | static void dsa_remove_dst(struct dsa_switch_tree *dst) |
781 | { | 806 | { |
782 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
783 | int i; | 807 | int i; |
784 | 808 | ||
785 | if (dst->link_poll_needed) | 809 | if (dst->link_poll_needed) |
@@ -793,8 +817,14 @@ static int dsa_remove(struct platform_device *pdev) | |||
793 | if (ds != NULL) | 817 | if (ds != NULL) |
794 | dsa_switch_destroy(ds); | 818 | dsa_switch_destroy(ds); |
795 | } | 819 | } |
820 | } | ||
821 | |||
822 | static int dsa_remove(struct platform_device *pdev) | ||
823 | { | ||
824 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
796 | 825 | ||
797 | dsa_of_remove(pdev); | 826 | dsa_remove_dst(dst); |
827 | dsa_of_remove(&pdev->dev); | ||
798 | 828 | ||
799 | return 0; | 829 | return 0; |
800 | } | 830 | } |
@@ -821,6 +851,10 @@ static struct packet_type dsa_pack_type __read_mostly = { | |||
821 | .func = dsa_switch_rcv, | 851 | .func = dsa_switch_rcv, |
822 | }; | 852 | }; |
823 | 853 | ||
854 | static struct notifier_block dsa_netdevice_nb __read_mostly = { | ||
855 | .notifier_call = dsa_slave_netdevice_event, | ||
856 | }; | ||
857 | |||
824 | #ifdef CONFIG_PM_SLEEP | 858 | #ifdef CONFIG_PM_SLEEP |
825 | static int dsa_suspend(struct device *d) | 859 | static int dsa_suspend(struct device *d) |
826 | { | 860 | { |
@@ -879,6 +913,8 @@ static int __init dsa_init_module(void) | |||
879 | { | 913 | { |
880 | int rc; | 914 | int rc; |
881 | 915 | ||
916 | register_netdevice_notifier(&dsa_netdevice_nb); | ||
917 | |||
882 | rc = platform_driver_register(&dsa_driver); | 918 | rc = platform_driver_register(&dsa_driver); |
883 | if (rc) | 919 | if (rc) |
884 | return rc; | 920 | return rc; |
@@ -891,6 +927,7 @@ module_init(dsa_init_module); | |||
891 | 927 | ||
892 | static void __exit dsa_cleanup_module(void) | 928 | static void __exit dsa_cleanup_module(void) |
893 | { | 929 | { |
930 | unregister_netdevice_notifier(&dsa_netdevice_nb); | ||
894 | dev_remove_pack(&dsa_pack_type); | 931 | dev_remove_pack(&dsa_pack_type); |
895 | platform_driver_unregister(&dsa_driver); | 932 | platform_driver_unregister(&dsa_driver); |
896 | } | 933 | } |