diff options
Diffstat (limited to 'net/dsa/dsa.c')
-rw-r--r-- | net/dsa/dsa.c | 274 |
1 files changed, 151 insertions, 123 deletions
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 2173402d87e0..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]); |
@@ -501,12 +513,10 @@ static struct net_device *dev_to_net_device(struct device *dev) | |||
501 | #ifdef CONFIG_OF | 513 | #ifdef CONFIG_OF |
502 | static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, | 514 | static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, |
503 | struct dsa_chip_data *cd, | 515 | struct dsa_chip_data *cd, |
504 | int chip_index, | 516 | int chip_index, int port_index, |
505 | struct device_node *link) | 517 | struct device_node *link) |
506 | { | 518 | { |
507 | int ret; | ||
508 | const __be32 *reg; | 519 | const __be32 *reg; |
509 | int link_port_addr; | ||
510 | int link_sw_addr; | 520 | int link_sw_addr; |
511 | struct device_node *parent_sw; | 521 | struct device_node *parent_sw; |
512 | int len; | 522 | int len; |
@@ -519,6 +529,10 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, | |||
519 | if (!reg || (len != sizeof(*reg) * 2)) | 529 | if (!reg || (len != sizeof(*reg) * 2)) |
520 | return -EINVAL; | 530 | return -EINVAL; |
521 | 531 | ||
532 | /* | ||
533 | * Get the destination switch number from the second field of its 'reg' | ||
534 | * property, i.e. for "reg = <0x19 1>" sw_addr is '1'. | ||
535 | */ | ||
522 | link_sw_addr = be32_to_cpup(reg + 1); | 536 | link_sw_addr = be32_to_cpup(reg + 1); |
523 | 537 | ||
524 | if (link_sw_addr >= pd->nr_chips) | 538 | if (link_sw_addr >= pd->nr_chips) |
@@ -535,20 +549,9 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, | |||
535 | memset(cd->rtable, -1, pd->nr_chips * sizeof(s8)); | 549 | memset(cd->rtable, -1, pd->nr_chips * sizeof(s8)); |
536 | } | 550 | } |
537 | 551 | ||
538 | reg = of_get_property(link, "reg", NULL); | 552 | cd->rtable[link_sw_addr] = port_index; |
539 | if (!reg) { | ||
540 | ret = -EINVAL; | ||
541 | goto out; | ||
542 | } | ||
543 | |||
544 | link_port_addr = be32_to_cpup(reg); | ||
545 | |||
546 | cd->rtable[link_sw_addr] = link_port_addr; | ||
547 | 553 | ||
548 | return 0; | 554 | return 0; |
549 | out: | ||
550 | kfree(cd->rtable); | ||
551 | return ret; | ||
552 | } | 555 | } |
553 | 556 | ||
554 | static void dsa_of_free_platform_data(struct dsa_platform_data *pd) | 557 | static void dsa_of_free_platform_data(struct dsa_platform_data *pd) |
@@ -567,12 +570,12 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd) | |||
567 | kfree(pd->chip); | 570 | kfree(pd->chip); |
568 | } | 571 | } |
569 | 572 | ||
570 | static int dsa_of_probe(struct platform_device *pdev) | 573 | static int dsa_of_probe(struct device *dev) |
571 | { | 574 | { |
572 | struct device_node *np = pdev->dev.of_node; | 575 | struct device_node *np = dev->of_node; |
573 | struct device_node *child, *mdio, *ethernet, *port, *link; | 576 | struct device_node *child, *mdio, *ethernet, *port, *link; |
574 | struct mii_bus *mdio_bus; | 577 | struct mii_bus *mdio_bus; |
575 | struct platform_device *ethernet_dev; | 578 | struct net_device *ethernet_dev; |
576 | struct dsa_platform_data *pd; | 579 | struct dsa_platform_data *pd; |
577 | struct dsa_chip_data *cd; | 580 | struct dsa_chip_data *cd; |
578 | const char *port_name; | 581 | const char *port_name; |
@@ -587,22 +590,22 @@ static int dsa_of_probe(struct platform_device *pdev) | |||
587 | 590 | ||
588 | mdio_bus = of_mdio_find_bus(mdio); | 591 | mdio_bus = of_mdio_find_bus(mdio); |
589 | if (!mdio_bus) | 592 | if (!mdio_bus) |
590 | return -EINVAL; | 593 | return -EPROBE_DEFER; |
591 | 594 | ||
592 | ethernet = of_parse_phandle(np, "dsa,ethernet", 0); | 595 | ethernet = of_parse_phandle(np, "dsa,ethernet", 0); |
593 | if (!ethernet) | 596 | if (!ethernet) |
594 | return -EINVAL; | 597 | return -EINVAL; |
595 | 598 | ||
596 | ethernet_dev = of_find_device_by_node(ethernet); | 599 | ethernet_dev = of_find_net_device_by_node(ethernet); |
597 | if (!ethernet_dev) | 600 | if (!ethernet_dev) |
598 | return -ENODEV; | 601 | return -EPROBE_DEFER; |
599 | 602 | ||
600 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | 603 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); |
601 | if (!pd) | 604 | if (!pd) |
602 | return -ENOMEM; | 605 | return -ENOMEM; |
603 | 606 | ||
604 | pdev->dev.platform_data = pd; | 607 | dev->platform_data = pd; |
605 | pd->netdev = ðernet_dev->dev; | 608 | pd->of_netdev = ethernet_dev; |
606 | pd->nr_chips = of_get_available_child_count(np); | 609 | pd->nr_chips = of_get_available_child_count(np); |
607 | if (pd->nr_chips > DSA_MAX_SWITCHES) | 610 | if (pd->nr_chips > DSA_MAX_SWITCHES) |
608 | pd->nr_chips = DSA_MAX_SWITCHES; | 611 | pd->nr_chips = DSA_MAX_SWITCHES; |
@@ -658,7 +661,7 @@ static int dsa_of_probe(struct platform_device *pdev) | |||
658 | if (!strcmp(port_name, "dsa") && link && | 661 | if (!strcmp(port_name, "dsa") && link && |
659 | pd->nr_chips > 1) { | 662 | pd->nr_chips > 1) { |
660 | ret = dsa_of_setup_routing_table(pd, cd, | 663 | ret = dsa_of_setup_routing_table(pd, cd, |
661 | chip_index, link); | 664 | chip_index, port_index, link); |
662 | if (ret) | 665 | if (ret) |
663 | goto out_free_chip; | 666 | goto out_free_chip; |
664 | } | 667 | } |
@@ -674,72 +677,35 @@ out_free_chip: | |||
674 | dsa_of_free_platform_data(pd); | 677 | dsa_of_free_platform_data(pd); |
675 | out_free: | 678 | out_free: |
676 | kfree(pd); | 679 | kfree(pd); |
677 | pdev->dev.platform_data = NULL; | 680 | dev->platform_data = NULL; |
678 | return ret; | 681 | return ret; |
679 | } | 682 | } |
680 | 683 | ||
681 | static void dsa_of_remove(struct platform_device *pdev) | 684 | static void dsa_of_remove(struct device *dev) |
682 | { | 685 | { |
683 | struct dsa_platform_data *pd = pdev->dev.platform_data; | 686 | struct dsa_platform_data *pd = dev->platform_data; |
684 | 687 | ||
685 | if (!pdev->dev.of_node) | 688 | if (!dev->of_node) |
686 | return; | 689 | return; |
687 | 690 | ||
688 | dsa_of_free_platform_data(pd); | 691 | dsa_of_free_platform_data(pd); |
689 | kfree(pd); | 692 | kfree(pd); |
690 | } | 693 | } |
691 | #else | 694 | #else |
692 | static inline int dsa_of_probe(struct platform_device *pdev) | 695 | static inline int dsa_of_probe(struct device *dev) |
693 | { | 696 | { |
694 | return 0; | 697 | return 0; |
695 | } | 698 | } |
696 | 699 | ||
697 | static inline void dsa_of_remove(struct platform_device *pdev) | 700 | static inline void dsa_of_remove(struct device *dev) |
698 | { | 701 | { |
699 | } | 702 | } |
700 | #endif | 703 | #endif |
701 | 704 | ||
702 | 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) | ||
703 | { | 707 | { |
704 | struct dsa_platform_data *pd = pdev->dev.platform_data; | 708 | int i; |
705 | struct net_device *dev; | ||
706 | struct dsa_switch_tree *dst; | ||
707 | int i, ret; | ||
708 | |||
709 | pr_notice_once("Distributed Switch Architecture driver version %s\n", | ||
710 | dsa_driver_version); | ||
711 | |||
712 | if (pdev->dev.of_node) { | ||
713 | ret = dsa_of_probe(pdev); | ||
714 | if (ret) | ||
715 | return ret; | ||
716 | |||
717 | pd = pdev->dev.platform_data; | ||
718 | } | ||
719 | |||
720 | if (pd == NULL || pd->netdev == NULL) | ||
721 | return -EINVAL; | ||
722 | |||
723 | dev = dev_to_net_device(pd->netdev); | ||
724 | if (dev == NULL) { | ||
725 | ret = -EINVAL; | ||
726 | goto out; | ||
727 | } | ||
728 | |||
729 | if (dev->dsa_ptr != NULL) { | ||
730 | dev_put(dev); | ||
731 | ret = -EEXIST; | ||
732 | goto out; | ||
733 | } | ||
734 | |||
735 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | ||
736 | if (dst == NULL) { | ||
737 | dev_put(dev); | ||
738 | ret = -ENOMEM; | ||
739 | goto out; | ||
740 | } | ||
741 | |||
742 | platform_set_drvdata(pdev, dst); | ||
743 | 709 | ||
744 | dst->pd = pd; | 710 | dst->pd = pd; |
745 | dst->master_netdev = dev; | 711 | dst->master_netdev = dev; |
@@ -749,7 +715,7 @@ static int dsa_probe(struct platform_device *pdev) | |||
749 | for (i = 0; i < pd->nr_chips; i++) { | 715 | for (i = 0; i < pd->nr_chips; i++) { |
750 | struct dsa_switch *ds; | 716 | struct dsa_switch *ds; |
751 | 717 | ||
752 | 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); |
753 | if (IS_ERR(ds)) { | 719 | if (IS_ERR(ds)) { |
754 | 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", |
755 | i, PTR_ERR(ds)); | 721 | i, PTR_ERR(ds)); |
@@ -777,18 +743,67 @@ static int dsa_probe(struct platform_device *pdev) | |||
777 | dst->link_poll_timer.expires = round_jiffies(jiffies + HZ); | 743 | dst->link_poll_timer.expires = round_jiffies(jiffies + HZ); |
778 | add_timer(&dst->link_poll_timer); | 744 | add_timer(&dst->link_poll_timer); |
779 | } | 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); | ||
780 | 796 | ||
781 | return 0; | 797 | return 0; |
782 | 798 | ||
783 | out: | 799 | out: |
784 | dsa_of_remove(pdev); | 800 | dsa_of_remove(&pdev->dev); |
785 | 801 | ||
786 | return ret; | 802 | return ret; |
787 | } | 803 | } |
788 | 804 | ||
789 | static int dsa_remove(struct platform_device *pdev) | 805 | static void dsa_remove_dst(struct dsa_switch_tree *dst) |
790 | { | 806 | { |
791 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
792 | int i; | 807 | int i; |
793 | 808 | ||
794 | if (dst->link_poll_needed) | 809 | if (dst->link_poll_needed) |
@@ -802,8 +817,14 @@ static int dsa_remove(struct platform_device *pdev) | |||
802 | if (ds != NULL) | 817 | if (ds != NULL) |
803 | dsa_switch_destroy(ds); | 818 | dsa_switch_destroy(ds); |
804 | } | 819 | } |
820 | } | ||
805 | 821 | ||
806 | dsa_of_remove(pdev); | 822 | static int dsa_remove(struct platform_device *pdev) |
823 | { | ||
824 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
825 | |||
826 | dsa_remove_dst(dst); | ||
827 | dsa_of_remove(&pdev->dev); | ||
807 | 828 | ||
808 | return 0; | 829 | return 0; |
809 | } | 830 | } |
@@ -830,6 +851,10 @@ static struct packet_type dsa_pack_type __read_mostly = { | |||
830 | .func = dsa_switch_rcv, | 851 | .func = dsa_switch_rcv, |
831 | }; | 852 | }; |
832 | 853 | ||
854 | static struct notifier_block dsa_netdevice_nb __read_mostly = { | ||
855 | .notifier_call = dsa_slave_netdevice_event, | ||
856 | }; | ||
857 | |||
833 | #ifdef CONFIG_PM_SLEEP | 858 | #ifdef CONFIG_PM_SLEEP |
834 | static int dsa_suspend(struct device *d) | 859 | static int dsa_suspend(struct device *d) |
835 | { | 860 | { |
@@ -888,6 +913,8 @@ static int __init dsa_init_module(void) | |||
888 | { | 913 | { |
889 | int rc; | 914 | int rc; |
890 | 915 | ||
916 | register_netdevice_notifier(&dsa_netdevice_nb); | ||
917 | |||
891 | rc = platform_driver_register(&dsa_driver); | 918 | rc = platform_driver_register(&dsa_driver); |
892 | if (rc) | 919 | if (rc) |
893 | return rc; | 920 | return rc; |
@@ -900,6 +927,7 @@ module_init(dsa_init_module); | |||
900 | 927 | ||
901 | static void __exit dsa_cleanup_module(void) | 928 | static void __exit dsa_cleanup_module(void) |
902 | { | 929 | { |
930 | unregister_netdevice_notifier(&dsa_netdevice_nb); | ||
903 | dev_remove_pack(&dsa_pack_type); | 931 | dev_remove_pack(&dsa_pack_type); |
904 | platform_driver_unregister(&dsa_driver); | 932 | platform_driver_unregister(&dsa_driver); |
905 | } | 933 | } |