diff options
Diffstat (limited to 'net/dsa/dsa.c')
-rw-r--r-- | net/dsa/dsa.c | 186 |
1 files changed, 148 insertions, 38 deletions
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 0a49632fac47..22f34cf4cb27 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c | |||
@@ -10,7 +10,6 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/list.h> | 12 | #include <linux/list.h> |
13 | #include <linux/netdevice.h> | ||
14 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
15 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
16 | #include <linux/module.h> | 15 | #include <linux/module.h> |
@@ -44,7 +43,7 @@ void unregister_switch_driver(struct dsa_switch_driver *drv) | |||
44 | EXPORT_SYMBOL_GPL(unregister_switch_driver); | 43 | EXPORT_SYMBOL_GPL(unregister_switch_driver); |
45 | 44 | ||
46 | static struct dsa_switch_driver * | 45 | static struct dsa_switch_driver * |
47 | dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name) | 46 | dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name) |
48 | { | 47 | { |
49 | struct dsa_switch_driver *ret; | 48 | struct dsa_switch_driver *ret; |
50 | struct list_head *list; | 49 | struct list_head *list; |
@@ -59,7 +58,7 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name) | |||
59 | 58 | ||
60 | drv = list_entry(list, struct dsa_switch_driver, list); | 59 | drv = list_entry(list, struct dsa_switch_driver, list); |
61 | 60 | ||
62 | name = drv->probe(bus, sw_addr); | 61 | name = drv->probe(host_dev, sw_addr); |
63 | if (name != NULL) { | 62 | if (name != NULL) { |
64 | ret = drv; | 63 | ret = drv; |
65 | break; | 64 | break; |
@@ -76,7 +75,7 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name) | |||
76 | /* basic switch operations **************************************************/ | 75 | /* basic switch operations **************************************************/ |
77 | static struct dsa_switch * | 76 | static struct dsa_switch * |
78 | dsa_switch_setup(struct dsa_switch_tree *dst, int index, | 77 | dsa_switch_setup(struct dsa_switch_tree *dst, int index, |
79 | struct device *parent, struct mii_bus *bus) | 78 | struct device *parent, struct device *host_dev) |
80 | { | 79 | { |
81 | struct dsa_chip_data *pd = dst->pd->chip + index; | 80 | struct dsa_chip_data *pd = dst->pd->chip + index; |
82 | struct dsa_switch_driver *drv; | 81 | struct dsa_switch_driver *drv; |
@@ -89,7 +88,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
89 | /* | 88 | /* |
90 | * Probe for switch model. | 89 | * Probe for switch model. |
91 | */ | 90 | */ |
92 | drv = dsa_switch_probe(bus, pd->sw_addr, &name); | 91 | drv = dsa_switch_probe(host_dev, pd->sw_addr, &name); |
93 | if (drv == NULL) { | 92 | if (drv == NULL) { |
94 | printk(KERN_ERR "%s[%d]: could not detect attached switch\n", | 93 | printk(KERN_ERR "%s[%d]: could not detect attached switch\n", |
95 | dst->master_netdev->name, index); | 94 | dst->master_netdev->name, index); |
@@ -110,8 +109,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
110 | ds->index = index; | 109 | ds->index = index; |
111 | ds->pd = dst->pd->chip + index; | 110 | ds->pd = dst->pd->chip + index; |
112 | ds->drv = drv; | 111 | ds->drv = drv; |
113 | ds->master_mii_bus = bus; | 112 | ds->master_dev = host_dev; |
114 | |||
115 | 113 | ||
116 | /* | 114 | /* |
117 | * Validate supplied switch configuration. | 115 | * Validate supplied switch configuration. |
@@ -144,14 +142,44 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index, | |||
144 | goto out; | 142 | goto out; |
145 | } | 143 | } |
146 | 144 | ||
145 | /* Make the built-in MII bus mask match the number of ports, | ||
146 | * switch drivers can override this later | ||
147 | */ | ||
148 | ds->phys_mii_mask = ds->phys_port_mask; | ||
149 | |||
147 | /* | 150 | /* |
148 | * If the CPU connects to this switch, set the switch tree | 151 | * If the CPU connects to this switch, set the switch tree |
149 | * tagging protocol to the preferred tagging format of this | 152 | * tagging protocol to the preferred tagging format of this |
150 | * switch. | 153 | * switch. |
151 | */ | 154 | */ |
152 | if (ds->dst->cpu_switch == index) | 155 | if (dst->cpu_switch == index) { |
153 | ds->dst->tag_protocol = drv->tag_protocol; | 156 | switch (drv->tag_protocol) { |
157 | #ifdef CONFIG_NET_DSA_TAG_DSA | ||
158 | case DSA_TAG_PROTO_DSA: | ||
159 | dst->rcv = dsa_netdev_ops.rcv; | ||
160 | break; | ||
161 | #endif | ||
162 | #ifdef CONFIG_NET_DSA_TAG_EDSA | ||
163 | case DSA_TAG_PROTO_EDSA: | ||
164 | dst->rcv = edsa_netdev_ops.rcv; | ||
165 | break; | ||
166 | #endif | ||
167 | #ifdef CONFIG_NET_DSA_TAG_TRAILER | ||
168 | case DSA_TAG_PROTO_TRAILER: | ||
169 | dst->rcv = trailer_netdev_ops.rcv; | ||
170 | break; | ||
171 | #endif | ||
172 | #ifdef CONFIG_NET_DSA_TAG_BRCM | ||
173 | case DSA_TAG_PROTO_BRCM: | ||
174 | dst->rcv = brcm_netdev_ops.rcv; | ||
175 | break; | ||
176 | #endif | ||
177 | default: | ||
178 | break; | ||
179 | } | ||
154 | 180 | ||
181 | dst->tag_protocol = drv->tag_protocol; | ||
182 | } | ||
155 | 183 | ||
156 | /* | 184 | /* |
157 | * Do basic register setup. | 185 | * Do basic register setup. |
@@ -210,6 +238,51 @@ static void dsa_switch_destroy(struct dsa_switch *ds) | |||
210 | { | 238 | { |
211 | } | 239 | } |
212 | 240 | ||
241 | #ifdef CONFIG_PM_SLEEP | ||
242 | static int dsa_switch_suspend(struct dsa_switch *ds) | ||
243 | { | ||
244 | int i, ret = 0; | ||
245 | |||
246 | /* Suspend slave network devices */ | ||
247 | for (i = 0; i < DSA_MAX_PORTS; i++) { | ||
248 | if (!(ds->phys_port_mask & (1 << i))) | ||
249 | continue; | ||
250 | |||
251 | ret = dsa_slave_suspend(ds->ports[i]); | ||
252 | if (ret) | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | if (ds->drv->suspend) | ||
257 | ret = ds->drv->suspend(ds); | ||
258 | |||
259 | return ret; | ||
260 | } | ||
261 | |||
262 | static int dsa_switch_resume(struct dsa_switch *ds) | ||
263 | { | ||
264 | int i, ret = 0; | ||
265 | |||
266 | if (ds->drv->resume) | ||
267 | ret = ds->drv->resume(ds); | ||
268 | |||
269 | if (ret) | ||
270 | return ret; | ||
271 | |||
272 | /* Resume slave network devices */ | ||
273 | for (i = 0; i < DSA_MAX_PORTS; i++) { | ||
274 | if (!(ds->phys_port_mask & (1 << i))) | ||
275 | continue; | ||
276 | |||
277 | ret = dsa_slave_resume(ds->ports[i]); | ||
278 | if (ret) | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | return 0; | ||
283 | } | ||
284 | #endif | ||
285 | |||
213 | 286 | ||
214 | /* link polling *************************************************************/ | 287 | /* link polling *************************************************************/ |
215 | static void dsa_link_poll_work(struct work_struct *ugly) | 288 | static void dsa_link_poll_work(struct work_struct *ugly) |
@@ -256,7 +329,7 @@ static struct device *dev_find_class(struct device *parent, char *class) | |||
256 | return device_find_child(parent, class, dev_is_class); | 329 | return device_find_child(parent, class, dev_is_class); |
257 | } | 330 | } |
258 | 331 | ||
259 | static struct mii_bus *dev_to_mii_bus(struct device *dev) | 332 | struct mii_bus *dsa_host_dev_to_mii_bus(struct device *dev) |
260 | { | 333 | { |
261 | struct device *d; | 334 | struct device *d; |
262 | 335 | ||
@@ -272,6 +345,7 @@ static struct mii_bus *dev_to_mii_bus(struct device *dev) | |||
272 | 345 | ||
273 | return NULL; | 346 | return NULL; |
274 | } | 347 | } |
348 | EXPORT_SYMBOL_GPL(dsa_host_dev_to_mii_bus); | ||
275 | 349 | ||
276 | static struct net_device *dev_to_net_device(struct device *dev) | 350 | static struct net_device *dev_to_net_device(struct device *dev) |
277 | { | 351 | { |
@@ -410,7 +484,8 @@ static int dsa_of_probe(struct platform_device *pdev) | |||
410 | chip_index++; | 484 | chip_index++; |
411 | cd = &pd->chip[chip_index]; | 485 | cd = &pd->chip[chip_index]; |
412 | 486 | ||
413 | cd->mii_bus = &mdio_bus->dev; | 487 | cd->of_node = child; |
488 | cd->host_dev = &mdio_bus->dev; | ||
414 | 489 | ||
415 | sw_addr = of_get_property(child, "reg", NULL); | 490 | sw_addr = of_get_property(child, "reg", NULL); |
416 | if (!sw_addr) | 491 | if (!sw_addr) |
@@ -431,6 +506,8 @@ static int dsa_of_probe(struct platform_device *pdev) | |||
431 | if (!port_name) | 506 | if (!port_name) |
432 | continue; | 507 | continue; |
433 | 508 | ||
509 | cd->port_dn[port_index] = port; | ||
510 | |||
434 | cd->port_names[port_index] = kstrdup(port_name, | 511 | cd->port_names[port_index] = kstrdup(port_name, |
435 | GFP_KERNEL); | 512 | GFP_KERNEL); |
436 | if (!cd->port_names[port_index]) { | 513 | if (!cd->port_names[port_index]) { |
@@ -534,17 +611,9 @@ static int dsa_probe(struct platform_device *pdev) | |||
534 | dst->cpu_port = -1; | 611 | dst->cpu_port = -1; |
535 | 612 | ||
536 | for (i = 0; i < pd->nr_chips; i++) { | 613 | for (i = 0; i < pd->nr_chips; i++) { |
537 | struct mii_bus *bus; | ||
538 | struct dsa_switch *ds; | 614 | struct dsa_switch *ds; |
539 | 615 | ||
540 | bus = dev_to_mii_bus(pd->chip[i].mii_bus); | 616 | ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev); |
541 | if (bus == NULL) { | ||
542 | printk(KERN_ERR "%s[%d]: no mii bus found for " | ||
543 | "dsa switch\n", dev->name, i); | ||
544 | continue; | ||
545 | } | ||
546 | |||
547 | ds = dsa_switch_setup(dst, i, &pdev->dev, bus); | ||
548 | if (IS_ERR(ds)) { | 617 | if (IS_ERR(ds)) { |
549 | printk(KERN_ERR "%s[%d]: couldn't create dsa switch " | 618 | printk(KERN_ERR "%s[%d]: couldn't create dsa switch " |
550 | "instance (error %ld)\n", dev->name, i, | 619 | "instance (error %ld)\n", dev->name, i, |
@@ -608,7 +677,62 @@ static void dsa_shutdown(struct platform_device *pdev) | |||
608 | { | 677 | { |
609 | } | 678 | } |
610 | 679 | ||
680 | static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, | ||
681 | struct packet_type *pt, struct net_device *orig_dev) | ||
682 | { | ||
683 | struct dsa_switch_tree *dst = dev->dsa_ptr; | ||
684 | |||
685 | if (unlikely(dst == NULL)) { | ||
686 | kfree_skb(skb); | ||
687 | return 0; | ||
688 | } | ||
689 | |||
690 | return dst->rcv(skb, dev, pt, orig_dev); | ||
691 | } | ||
692 | |||
693 | static struct packet_type dsa_pack_type __read_mostly = { | ||
694 | .type = cpu_to_be16(ETH_P_XDSA), | ||
695 | .func = dsa_switch_rcv, | ||
696 | }; | ||
697 | |||
698 | #ifdef CONFIG_PM_SLEEP | ||
699 | static int dsa_suspend(struct device *d) | ||
700 | { | ||
701 | struct platform_device *pdev = to_platform_device(d); | ||
702 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
703 | int i, ret = 0; | ||
704 | |||
705 | for (i = 0; i < dst->pd->nr_chips; i++) { | ||
706 | struct dsa_switch *ds = dst->ds[i]; | ||
707 | |||
708 | if (ds != NULL) | ||
709 | ret = dsa_switch_suspend(ds); | ||
710 | } | ||
711 | |||
712 | return ret; | ||
713 | } | ||
714 | |||
715 | static int dsa_resume(struct device *d) | ||
716 | { | ||
717 | struct platform_device *pdev = to_platform_device(d); | ||
718 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
719 | int i, ret = 0; | ||
720 | |||
721 | for (i = 0; i < dst->pd->nr_chips; i++) { | ||
722 | struct dsa_switch *ds = dst->ds[i]; | ||
723 | |||
724 | if (ds != NULL) | ||
725 | ret = dsa_switch_resume(ds); | ||
726 | } | ||
727 | |||
728 | return ret; | ||
729 | } | ||
730 | #endif | ||
731 | |||
732 | static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume); | ||
733 | |||
611 | static const struct of_device_id dsa_of_match_table[] = { | 734 | static const struct of_device_id dsa_of_match_table[] = { |
735 | { .compatible = "brcm,bcm7445-switch-v4.0" }, | ||
612 | { .compatible = "marvell,dsa", }, | 736 | { .compatible = "marvell,dsa", }, |
613 | {} | 737 | {} |
614 | }; | 738 | }; |
@@ -622,6 +746,7 @@ static struct platform_driver dsa_driver = { | |||
622 | .name = "dsa", | 746 | .name = "dsa", |
623 | .owner = THIS_MODULE, | 747 | .owner = THIS_MODULE, |
624 | .of_match_table = dsa_of_match_table, | 748 | .of_match_table = dsa_of_match_table, |
749 | .pm = &dsa_pm_ops, | ||
625 | }, | 750 | }, |
626 | }; | 751 | }; |
627 | 752 | ||
@@ -633,30 +758,15 @@ static int __init dsa_init_module(void) | |||
633 | if (rc) | 758 | if (rc) |
634 | return rc; | 759 | return rc; |
635 | 760 | ||
636 | #ifdef CONFIG_NET_DSA_TAG_DSA | 761 | dev_add_pack(&dsa_pack_type); |
637 | dev_add_pack(&dsa_packet_type); | 762 | |
638 | #endif | ||
639 | #ifdef CONFIG_NET_DSA_TAG_EDSA | ||
640 | dev_add_pack(&edsa_packet_type); | ||
641 | #endif | ||
642 | #ifdef CONFIG_NET_DSA_TAG_TRAILER | ||
643 | dev_add_pack(&trailer_packet_type); | ||
644 | #endif | ||
645 | return 0; | 763 | return 0; |
646 | } | 764 | } |
647 | module_init(dsa_init_module); | 765 | module_init(dsa_init_module); |
648 | 766 | ||
649 | static void __exit dsa_cleanup_module(void) | 767 | static void __exit dsa_cleanup_module(void) |
650 | { | 768 | { |
651 | #ifdef CONFIG_NET_DSA_TAG_TRAILER | 769 | dev_remove_pack(&dsa_pack_type); |
652 | dev_remove_pack(&trailer_packet_type); | ||
653 | #endif | ||
654 | #ifdef CONFIG_NET_DSA_TAG_EDSA | ||
655 | dev_remove_pack(&edsa_packet_type); | ||
656 | #endif | ||
657 | #ifdef CONFIG_NET_DSA_TAG_DSA | ||
658 | dev_remove_pack(&dsa_packet_type); | ||
659 | #endif | ||
660 | platform_driver_unregister(&dsa_driver); | 770 | platform_driver_unregister(&dsa_driver); |
661 | } | 771 | } |
662 | module_exit(dsa_cleanup_module); | 772 | module_exit(dsa_cleanup_module); |