aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/dsa.c
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2009-03-20 05:52:09 -0400
committerDavid S. Miller <davem@davemloft.net>2009-03-21 22:06:54 -0400
commite84665c9cb4db963393fafad6fefe5efdd7e4a09 (patch)
tree545c4a2a63a77b853e3f34609d86b346fe61baf4 /net/dsa/dsa.c
parent076d3e10a54caa2c148de5732c126c7a31381d48 (diff)
dsa: add switch chip cascading support
The initial version of the DSA driver only supported a single switch chip per network interface, while DSA-capable switch chips can be interconnected to form a tree of switch chips. This patch adds support for multiple switch chips on a network interface. An example topology for a 16-port device with an embedded CPU is as follows: +-----+ +--------+ +--------+ | |eth0 10| switch |9 10| switch | | CPU +----------+ +-------+ | | | | chip 0 | | chip 1 | +-----+ +---++---+ +---++---+ || || || || ||1000baseT ||1000baseT ||ports 1-8 ||ports 9-16 This requires a couple of interdependent changes in the DSA layer: - The dsa platform driver data needs to be extended: there is still only one netdevice per DSA driver instance (eth0 in the example above), but each of the switch chips in the tree needs its own mii_bus device pointer, MII management bus address, and port name array. (include/net/dsa.h) The existing in-tree dsa users need some small changes to deal with this. (arch/arm) - The DSA and Ethertype DSA tagging modules need to be extended to use the DSA device ID field on receive and demultiplex the packet accordingly, and fill in the DSA device ID field on transmit according to which switch chip the packet is heading to. (net/dsa/tag_{dsa,edsa}.c) - The concept of "CPU port", which is the switch chip port that the CPU is connected to (port 10 on switch chip 0 in the example), needs to be extended with the concept of "upstream port", which is the port on the switch chip that will bring us one hop closer to the CPU (port 10 for both switch chips in the example above). - The dsa platform data needs to specify which ports on which switch chips are links to other switch chips, so that we can enable DSA tagging mode on them. (For inter-switch links, we always use non-EtherType DSA tagging, since it has lower overhead. The CPU link uses dsa or edsa tagging depending on what the 'root' switch chip supports.) This is done by specifying "dsa" for the given port in the port array. - The dsa platform data needs to be extended with information on via which port to reach any given switch chip from any given switch chip. This info is specified via the per-switch chip data struct ->rtable[] array, which gives the nexthop ports for each of the other switches in the tree. For the example topology above, the dsa platform data would look something like this: static struct dsa_chip_data sw[2] = { { .mii_bus = &foo, .sw_addr = 1, .port_names[0] = "p1", .port_names[1] = "p2", .port_names[2] = "p3", .port_names[3] = "p4", .port_names[4] = "p5", .port_names[5] = "p6", .port_names[6] = "p7", .port_names[7] = "p8", .port_names[9] = "dsa", .port_names[10] = "cpu", .rtable = (s8 []){ -1, 9, }, }, { .mii_bus = &foo, .sw_addr = 2, .port_names[0] = "p9", .port_names[1] = "p10", .port_names[2] = "p11", .port_names[3] = "p12", .port_names[4] = "p13", .port_names[5] = "p14", .port_names[6] = "p15", .port_names[7] = "p16", .port_names[10] = "dsa", .rtable = (s8 []){ 10, -1, }, }, }, static struct dsa_platform_data pd = { .netdev = &foo, .nr_switches = 2, .sw = sw, }; Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Gary Thomas <gary@mlbassoc.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa/dsa.c')
-rw-r--r--net/dsa/dsa.c177
1 files changed, 109 insertions, 68 deletions
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 33e99462023a..71489f69a42c 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * net/dsa/dsa.c - Hardware switch handling 2 * net/dsa/dsa.c - Hardware switch handling
3 * Copyright (c) 2008 Marvell Semiconductor 3 * Copyright (c) 2008-2009 Marvell Semiconductor
4 * 4 *
5 * This program is free software; you can redistribute it and/or modify 5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by 6 * it under the terms of the GNU General Public License as published by
@@ -67,12 +67,13 @@ dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name)
67 67
68/* basic switch operations **************************************************/ 68/* basic switch operations **************************************************/
69static struct dsa_switch * 69static struct dsa_switch *
70dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd, 70dsa_switch_setup(struct dsa_switch_tree *dst, int index,
71 struct mii_bus *bus, struct net_device *dev) 71 struct device *parent, struct mii_bus *bus)
72{ 72{
73 struct dsa_chip_data *pd = dst->pd->chip + index;
74 struct dsa_switch_driver *drv;
73 struct dsa_switch *ds; 75 struct dsa_switch *ds;
74 int ret; 76 int ret;
75 struct dsa_switch_driver *drv;
76 char *name; 77 char *name;
77 int i; 78 int i;
78 79
@@ -81,11 +82,12 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
81 */ 82 */
82 drv = dsa_switch_probe(bus, pd->sw_addr, &name); 83 drv = dsa_switch_probe(bus, pd->sw_addr, &name);
83 if (drv == NULL) { 84 if (drv == NULL) {
84 printk(KERN_ERR "%s: could not detect attached switch\n", 85 printk(KERN_ERR "%s[%d]: could not detect attached switch\n",
85 dev->name); 86 dst->master_netdev->name, index);
86 return ERR_PTR(-EINVAL); 87 return ERR_PTR(-EINVAL);
87 } 88 }
88 printk(KERN_INFO "%s: detected a %s switch\n", dev->name, name); 89 printk(KERN_INFO "%s[%d]: detected a %s switch\n",
90 dst->master_netdev->name, index, name);
89 91
90 92
91 /* 93 /*
@@ -95,18 +97,16 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
95 if (ds == NULL) 97 if (ds == NULL)
96 return ERR_PTR(-ENOMEM); 98 return ERR_PTR(-ENOMEM);
97 99
98 ds->pd = pd; 100 ds->dst = dst;
99 ds->master_netdev = dev; 101 ds->index = index;
100 ds->master_mii_bus = bus; 102 ds->pd = dst->pd->chip + index;
101
102 ds->drv = drv; 103 ds->drv = drv;
103 ds->tag_protocol = drv->tag_protocol; 104 ds->master_mii_bus = bus;
104 105
105 106
106 /* 107 /*
107 * Validate supplied switch configuration. 108 * Validate supplied switch configuration.
108 */ 109 */
109 ds->cpu_port = -1;
110 for (i = 0; i < DSA_MAX_PORTS; i++) { 110 for (i = 0; i < DSA_MAX_PORTS; i++) {
111 char *name; 111 char *name;
112 112
@@ -115,32 +115,28 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
115 continue; 115 continue;
116 116
117 if (!strcmp(name, "cpu")) { 117 if (!strcmp(name, "cpu")) {
118 if (ds->cpu_port != -1) { 118 if (dst->cpu_switch != -1) {
119 printk(KERN_ERR "multiple cpu ports?!\n"); 119 printk(KERN_ERR "multiple cpu ports?!\n");
120 ret = -EINVAL; 120 ret = -EINVAL;
121 goto out; 121 goto out;
122 } 122 }
123 ds->cpu_port = i; 123 dst->cpu_switch = index;
124 dst->cpu_port = i;
125 } else if (!strcmp(name, "dsa")) {
126 ds->dsa_port_mask |= 1 << i;
124 } else { 127 } else {
125 ds->valid_port_mask |= 1 << i; 128 ds->phys_port_mask |= 1 << i;
126 } 129 }
127 } 130 }
128 131
129 if (ds->cpu_port == -1) {
130 printk(KERN_ERR "no cpu port?!\n");
131 ret = -EINVAL;
132 goto out;
133 }
134
135 132
136 /* 133 /*
137 * If we use a tagging format that doesn't have an ethertype 134 * If the CPU connects to this switch, set the switch tree
138 * field, make sure that all packets from this point on get 135 * tagging protocol to the preferred tagging format of this
139 * sent to the tag format's receive function. (Which will 136 * switch.
140 * discard received packets until we set ds->ports[] below.)
141 */ 137 */
142 wmb(); 138 if (ds->dst->cpu_switch == index)
143 dev->dsa_ptr = (void *)ds; 139 ds->dst->tag_protocol = drv->tag_protocol;
144 140
145 141
146 /* 142 /*
@@ -150,7 +146,7 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
150 if (ret < 0) 146 if (ret < 0)
151 goto out; 147 goto out;
152 148
153 ret = drv->set_addr(ds, dev->dev_addr); 149 ret = drv->set_addr(ds, dst->master_netdev->dev_addr);
154 if (ret < 0) 150 if (ret < 0)
155 goto out; 151 goto out;
156 152
@@ -169,18 +165,18 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
169 /* 165 /*
170 * Create network devices for physical switch ports. 166 * Create network devices for physical switch ports.
171 */ 167 */
172 wmb();
173 for (i = 0; i < DSA_MAX_PORTS; i++) { 168 for (i = 0; i < DSA_MAX_PORTS; i++) {
174 struct net_device *slave_dev; 169 struct net_device *slave_dev;
175 170
176 if (!(ds->valid_port_mask & (1 << i))) 171 if (!(ds->phys_port_mask & (1 << i)))
177 continue; 172 continue;
178 173
179 slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]); 174 slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
180 if (slave_dev == NULL) { 175 if (slave_dev == NULL) {
181 printk(KERN_ERR "%s: can't create dsa slave " 176 printk(KERN_ERR "%s[%d]: can't create dsa "
182 "device for port %d(%s)\n", 177 "slave device for port %d(%s)\n",
183 dev->name, i, pd->port_names[i]); 178 dst->master_netdev->name,
179 index, i, pd->port_names[i]);
184 continue; 180 continue;
185 } 181 }
186 182
@@ -192,7 +188,6 @@ dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
192out_free: 188out_free:
193 mdiobus_free(ds->slave_mii_bus); 189 mdiobus_free(ds->slave_mii_bus);
194out: 190out:
195 dev->dsa_ptr = NULL;
196 kfree(ds); 191 kfree(ds);
197 return ERR_PTR(ret); 192 return ERR_PTR(ret);
198} 193}
@@ -212,35 +207,42 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
212 */ 207 */
213bool dsa_uses_dsa_tags(void *dsa_ptr) 208bool dsa_uses_dsa_tags(void *dsa_ptr)
214{ 209{
215 struct dsa_switch *ds = dsa_ptr; 210 struct dsa_switch_tree *dst = dsa_ptr;
216 211
217 return !!(ds->tag_protocol == htons(ETH_P_DSA)); 212 return !!(dst->tag_protocol == htons(ETH_P_DSA));
218} 213}
219 214
220bool dsa_uses_trailer_tags(void *dsa_ptr) 215bool dsa_uses_trailer_tags(void *dsa_ptr)
221{ 216{
222 struct dsa_switch *ds = dsa_ptr; 217 struct dsa_switch_tree *dst = dsa_ptr;
223 218
224 return !!(ds->tag_protocol == htons(ETH_P_TRAILER)); 219 return !!(dst->tag_protocol == htons(ETH_P_TRAILER));
225} 220}
226 221
227 222
228/* link polling *************************************************************/ 223/* link polling *************************************************************/
229static void dsa_link_poll_work(struct work_struct *ugly) 224static void dsa_link_poll_work(struct work_struct *ugly)
230{ 225{
231 struct dsa_switch *ds; 226 struct dsa_switch_tree *dst;
227 int i;
228
229 dst = container_of(ugly, struct dsa_switch_tree, link_poll_work);
232 230
233 ds = container_of(ugly, struct dsa_switch, link_poll_work); 231 for (i = 0; i < dst->pd->nr_chips; i++) {
232 struct dsa_switch *ds = dst->ds[i];
234 233
235 ds->drv->poll_link(ds); 234 if (ds != NULL && ds->drv->poll_link != NULL)
236 mod_timer(&ds->link_poll_timer, round_jiffies(jiffies + HZ)); 235 ds->drv->poll_link(ds);
236 }
237
238 mod_timer(&dst->link_poll_timer, round_jiffies(jiffies + HZ));
237} 239}
238 240
239static void dsa_link_poll_timer(unsigned long _ds) 241static void dsa_link_poll_timer(unsigned long _dst)
240{ 242{
241 struct dsa_switch *ds = (void *)_ds; 243 struct dsa_switch_tree *dst = (void *)_dst;
242 244
243 schedule_work(&ds->link_poll_work); 245 schedule_work(&dst->link_poll_work);
244} 246}
245 247
246 248
@@ -303,18 +305,14 @@ static int dsa_probe(struct platform_device *pdev)
303 static int dsa_version_printed; 305 static int dsa_version_printed;
304 struct dsa_platform_data *pd = pdev->dev.platform_data; 306 struct dsa_platform_data *pd = pdev->dev.platform_data;
305 struct net_device *dev; 307 struct net_device *dev;
306 struct mii_bus *bus; 308 struct dsa_switch_tree *dst;
307 struct dsa_switch *ds; 309 int i;
308 310
309 if (!dsa_version_printed++) 311 if (!dsa_version_printed++)
310 printk(KERN_NOTICE "Distributed Switch Architecture " 312 printk(KERN_NOTICE "Distributed Switch Architecture "
311 "driver version %s\n", dsa_driver_version); 313 "driver version %s\n", dsa_driver_version);
312 314
313 if (pd == NULL || pd->mii_bus == NULL || pd->netdev == NULL) 315 if (pd == NULL || pd->netdev == NULL)
314 return -EINVAL;
315
316 bus = dev_to_mii_bus(pd->mii_bus);
317 if (bus == NULL)
318 return -EINVAL; 316 return -EINVAL;
319 317
320 dev = dev_to_net_device(pd->netdev); 318 dev = dev_to_net_device(pd->netdev);
@@ -326,36 +324,79 @@ static int dsa_probe(struct platform_device *pdev)
326 return -EEXIST; 324 return -EEXIST;
327 } 325 }
328 326
329 ds = dsa_switch_setup(&pdev->dev, pd, bus, dev); 327 dst = kzalloc(sizeof(*dst), GFP_KERNEL);
330 if (IS_ERR(ds)) { 328 if (dst == NULL) {
331 dev_put(dev); 329 dev_put(dev);
332 return PTR_ERR(ds); 330 return -ENOMEM;
333 } 331 }
334 332
335 if (ds->drv->poll_link != NULL) { 333 platform_set_drvdata(pdev, dst);
336 INIT_WORK(&ds->link_poll_work, dsa_link_poll_work); 334
337 init_timer(&ds->link_poll_timer); 335 dst->pd = pd;
338 ds->link_poll_timer.data = (unsigned long)ds; 336 dst->master_netdev = dev;
339 ds->link_poll_timer.function = dsa_link_poll_timer; 337 dst->cpu_switch = -1;
340 ds->link_poll_timer.expires = round_jiffies(jiffies + HZ); 338 dst->cpu_port = -1;
341 add_timer(&ds->link_poll_timer); 339
340 for (i = 0; i < pd->nr_chips; i++) {
341 struct mii_bus *bus;
342 struct dsa_switch *ds;
343
344 bus = dev_to_mii_bus(pd->chip[i].mii_bus);
345 if (bus == NULL) {
346 printk(KERN_ERR "%s[%d]: no mii bus found for "
347 "dsa switch\n", dev->name, i);
348 continue;
349 }
350
351 ds = dsa_switch_setup(dst, i, &pdev->dev, bus);
352 if (IS_ERR(ds)) {
353 printk(KERN_ERR "%s[%d]: couldn't create dsa switch "
354 "instance (error %ld)\n", dev->name, i,
355 PTR_ERR(ds));
356 continue;
357 }
358
359 dst->ds[i] = ds;
360 if (ds->drv->poll_link != NULL)
361 dst->link_poll_needed = 1;
342 } 362 }
343 363
344 platform_set_drvdata(pdev, ds); 364 /*
365 * If we use a tagging format that doesn't have an ethertype
366 * field, make sure that all packets from this point on get
367 * sent to the tag format's receive function.
368 */
369 wmb();
370 dev->dsa_ptr = (void *)dst;
371
372 if (dst->link_poll_needed) {
373 INIT_WORK(&dst->link_poll_work, dsa_link_poll_work);
374 init_timer(&dst->link_poll_timer);
375 dst->link_poll_timer.data = (unsigned long)dst;
376 dst->link_poll_timer.function = dsa_link_poll_timer;
377 dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);
378 add_timer(&dst->link_poll_timer);
379 }
345 380
346 return 0; 381 return 0;
347} 382}
348 383
349static int dsa_remove(struct platform_device *pdev) 384static int dsa_remove(struct platform_device *pdev)
350{ 385{
351 struct dsa_switch *ds = platform_get_drvdata(pdev); 386 struct dsa_switch_tree *dst = platform_get_drvdata(pdev);
387 int i;
352 388
353 if (ds->drv->poll_link != NULL) 389 if (dst->link_poll_needed)
354 del_timer_sync(&ds->link_poll_timer); 390 del_timer_sync(&dst->link_poll_timer);
355 391
356 flush_scheduled_work(); 392 flush_scheduled_work();
357 393
358 dsa_switch_destroy(ds); 394 for (i = 0; i < dst->pd->nr_chips; i++) {
395 struct dsa_switch *ds = dst->ds[i];
396
397 if (ds != NULL)
398 dsa_switch_destroy(ds);
399 }
359 400
360 return 0; 401 return 0;
361} 402}