]>
Commit | Line | Data |
---|---|---|
83c0afae AL |
1 | /* |
2 | * net/dsa/dsa2.c - Hardware switch handling, binding version 2 | |
3 | * Copyright (c) 2008-2009 Marvell Semiconductor | |
4 | * Copyright (c) 2013 Florian Fainelli <[email protected]> | |
5 | * Copyright (c) 2016 Andrew Lunn <[email protected]> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | */ | |
12 | ||
13 | #include <linux/device.h> | |
14 | #include <linux/err.h> | |
15 | #include <linux/list.h> | |
c6e970a0 | 16 | #include <linux/netdevice.h> |
83c0afae AL |
17 | #include <linux/slab.h> |
18 | #include <linux/rtnetlink.h> | |
83c0afae AL |
19 | #include <linux/of.h> |
20 | #include <linux/of_net.h> | |
ea5dd34b | 21 | |
83c0afae AL |
22 | #include "dsa_priv.h" |
23 | ||
24 | static LIST_HEAD(dsa_switch_trees); | |
25 | static DEFINE_MUTEX(dsa2_mutex); | |
26 | ||
96567d5d AL |
27 | static const struct devlink_ops dsa_devlink_ops = { |
28 | }; | |
29 | ||
49463b7f | 30 | static struct dsa_switch_tree *dsa_get_dst(unsigned int index) |
83c0afae AL |
31 | { |
32 | struct dsa_switch_tree *dst; | |
33 | ||
34 | list_for_each_entry(dst, &dsa_switch_trees, list) | |
49463b7f | 35 | if (dst->index == index) { |
7a99cd6e | 36 | kref_get(&dst->refcount); |
83c0afae | 37 | return dst; |
7a99cd6e | 38 | } |
83c0afae AL |
39 | return NULL; |
40 | } | |
41 | ||
42 | static void dsa_free_dst(struct kref *ref) | |
43 | { | |
44 | struct dsa_switch_tree *dst = container_of(ref, struct dsa_switch_tree, | |
45 | refcount); | |
46 | ||
47 | list_del(&dst->list); | |
48 | kfree(dst); | |
49 | } | |
50 | ||
51 | static void dsa_put_dst(struct dsa_switch_tree *dst) | |
52 | { | |
53 | kref_put(&dst->refcount, dsa_free_dst); | |
54 | } | |
55 | ||
49463b7f | 56 | static struct dsa_switch_tree *dsa_add_dst(unsigned int index) |
83c0afae AL |
57 | { |
58 | struct dsa_switch_tree *dst; | |
59 | ||
60 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | |
61 | if (!dst) | |
62 | return NULL; | |
49463b7f | 63 | dst->index = index; |
83c0afae AL |
64 | INIT_LIST_HEAD(&dst->list); |
65 | list_add_tail(&dsa_switch_trees, &dst->list); | |
66 | kref_init(&dst->refcount); | |
67 | ||
68 | return dst; | |
69 | } | |
70 | ||
71 | static void dsa_dst_add_ds(struct dsa_switch_tree *dst, | |
72 | struct dsa_switch *ds, u32 index) | |
73 | { | |
74 | kref_get(&dst->refcount); | |
75 | dst->ds[index] = ds; | |
76 | } | |
77 | ||
78 | static void dsa_dst_del_ds(struct dsa_switch_tree *dst, | |
79 | struct dsa_switch *ds, u32 index) | |
80 | { | |
81 | dst->ds[index] = NULL; | |
82 | kref_put(&dst->refcount, dsa_free_dst); | |
83 | } | |
84 | ||
71e0bbde FF |
85 | /* For platform data configurations, we need to have a valid name argument to |
86 | * differentiate a disabled port from an enabled one | |
87 | */ | |
293784a8 | 88 | static bool dsa_port_is_valid(struct dsa_port *port) |
83c0afae | 89 | { |
6d4e5c57 | 90 | return port->type != DSA_PORT_TYPE_UNUSED; |
83c0afae AL |
91 | } |
92 | ||
293784a8 | 93 | static bool dsa_port_is_dsa(struct dsa_port *port) |
83c0afae | 94 | { |
6d4e5c57 | 95 | return port->type == DSA_PORT_TYPE_DSA; |
293784a8 FF |
96 | } |
97 | ||
98 | static bool dsa_port_is_cpu(struct dsa_port *port) | |
99 | { | |
6d4e5c57 | 100 | return port->type == DSA_PORT_TYPE_CPU; |
83c0afae AL |
101 | } |
102 | ||
3512a8e9 FF |
103 | static bool dsa_ds_find_port_dn(struct dsa_switch *ds, |
104 | struct device_node *port) | |
83c0afae AL |
105 | { |
106 | u32 index; | |
107 | ||
26895e29 | 108 | for (index = 0; index < ds->num_ports; index++) |
83c0afae AL |
109 | if (ds->ports[index].dn == port) |
110 | return true; | |
111 | return false; | |
112 | } | |
113 | ||
3512a8e9 FF |
114 | static struct dsa_switch *dsa_dst_find_port_dn(struct dsa_switch_tree *dst, |
115 | struct device_node *port) | |
83c0afae AL |
116 | { |
117 | struct dsa_switch *ds; | |
118 | u32 index; | |
119 | ||
120 | for (index = 0; index < DSA_MAX_SWITCHES; index++) { | |
121 | ds = dst->ds[index]; | |
122 | if (!ds) | |
123 | continue; | |
124 | ||
3512a8e9 | 125 | if (dsa_ds_find_port_dn(ds, port)) |
83c0afae AL |
126 | return ds; |
127 | } | |
128 | ||
129 | return NULL; | |
130 | } | |
131 | ||
132 | static int dsa_port_complete(struct dsa_switch_tree *dst, | |
133 | struct dsa_switch *src_ds, | |
293784a8 | 134 | struct dsa_port *port, |
83c0afae AL |
135 | u32 src_port) |
136 | { | |
137 | struct device_node *link; | |
138 | int index; | |
139 | struct dsa_switch *dst_ds; | |
140 | ||
141 | for (index = 0;; index++) { | |
293784a8 | 142 | link = of_parse_phandle(port->dn, "link", index); |
83c0afae AL |
143 | if (!link) |
144 | break; | |
145 | ||
3512a8e9 | 146 | dst_ds = dsa_dst_find_port_dn(dst, link); |
83c0afae AL |
147 | of_node_put(link); |
148 | ||
149 | if (!dst_ds) | |
150 | return 1; | |
151 | ||
152 | src_ds->rtable[dst_ds->index] = src_port; | |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
158 | /* A switch is complete if all the DSA ports phandles point to ports | |
159 | * known in the tree. A return value of 1 means the tree is not | |
160 | * complete. This is not an error condition. A value of 0 is | |
161 | * success. | |
162 | */ | |
163 | static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) | |
164 | { | |
293784a8 | 165 | struct dsa_port *port; |
83c0afae AL |
166 | u32 index; |
167 | int err; | |
168 | ||
26895e29 | 169 | for (index = 0; index < ds->num_ports; index++) { |
293784a8 FF |
170 | port = &ds->ports[index]; |
171 | if (!dsa_port_is_valid(port)) | |
83c0afae AL |
172 | continue; |
173 | ||
174 | if (!dsa_port_is_dsa(port)) | |
175 | continue; | |
176 | ||
177 | err = dsa_port_complete(dst, ds, port, index); | |
178 | if (err != 0) | |
179 | return err; | |
83c0afae AL |
180 | } |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | /* A tree is complete if all the DSA ports phandles point to ports | |
186 | * known in the tree. A return value of 1 means the tree is not | |
187 | * complete. This is not an error condition. A value of 0 is | |
188 | * success. | |
189 | */ | |
190 | static int dsa_dst_complete(struct dsa_switch_tree *dst) | |
191 | { | |
192 | struct dsa_switch *ds; | |
193 | u32 index; | |
194 | int err; | |
195 | ||
196 | for (index = 0; index < DSA_MAX_SWITCHES; index++) { | |
197 | ds = dst->ds[index]; | |
198 | if (!ds) | |
199 | continue; | |
200 | ||
201 | err = dsa_ds_complete(dst, ds); | |
202 | if (err != 0) | |
203 | return err; | |
204 | } | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
e41c1b50 | 209 | static int dsa_dsa_port_apply(struct dsa_port *port) |
83c0afae | 210 | { |
e41c1b50 | 211 | struct dsa_switch *ds = port->ds; |
83c0afae AL |
212 | int err; |
213 | ||
57ab1ca2 | 214 | err = dsa_port_fixed_link_register_of(port); |
83c0afae AL |
215 | if (err) { |
216 | dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n", | |
e41c1b50 | 217 | port->index, err); |
83c0afae AL |
218 | return err; |
219 | } | |
220 | ||
e41c1b50 | 221 | memset(&port->devlink_port, 0, sizeof(port->devlink_port)); |
96567d5d | 222 | |
e41c1b50 FF |
223 | return devlink_port_register(ds->devlink, &port->devlink_port, |
224 | port->index); | |
83c0afae AL |
225 | } |
226 | ||
e41c1b50 | 227 | static void dsa_dsa_port_unapply(struct dsa_port *port) |
83c0afae | 228 | { |
e41c1b50 | 229 | devlink_port_unregister(&port->devlink_port); |
57ab1ca2 | 230 | dsa_port_fixed_link_unregister_of(port); |
83c0afae AL |
231 | } |
232 | ||
e41c1b50 | 233 | static int dsa_cpu_port_apply(struct dsa_port *port) |
83c0afae | 234 | { |
e41c1b50 | 235 | struct dsa_switch *ds = port->ds; |
83c0afae AL |
236 | int err; |
237 | ||
57ab1ca2 | 238 | err = dsa_port_fixed_link_register_of(port); |
83c0afae AL |
239 | if (err) { |
240 | dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n", | |
e41c1b50 | 241 | port->index, err); |
83c0afae AL |
242 | return err; |
243 | } | |
244 | ||
e41c1b50 FF |
245 | memset(&port->devlink_port, 0, sizeof(port->devlink_port)); |
246 | err = devlink_port_register(ds->devlink, &port->devlink_port, | |
247 | port->index); | |
96567d5d | 248 | return err; |
83c0afae AL |
249 | } |
250 | ||
e41c1b50 | 251 | static void dsa_cpu_port_unapply(struct dsa_port *port) |
83c0afae | 252 | { |
e41c1b50 | 253 | devlink_port_unregister(&port->devlink_port); |
57ab1ca2 | 254 | dsa_port_fixed_link_unregister_of(port); |
83c0afae AL |
255 | } |
256 | ||
e41c1b50 | 257 | static int dsa_user_port_apply(struct dsa_port *port) |
83c0afae | 258 | { |
e41c1b50 | 259 | struct dsa_switch *ds = port->ds; |
83c0afae AL |
260 | int err; |
261 | ||
951259aa | 262 | err = dsa_slave_create(port); |
83c0afae AL |
263 | if (err) { |
264 | dev_warn(ds->dev, "Failed to create slave %d: %d\n", | |
e41c1b50 | 265 | port->index, err); |
f8b8b1cd | 266 | port->slave = NULL; |
83c0afae AL |
267 | return err; |
268 | } | |
269 | ||
e41c1b50 FF |
270 | memset(&port->devlink_port, 0, sizeof(port->devlink_port)); |
271 | err = devlink_port_register(ds->devlink, &port->devlink_port, | |
272 | port->index); | |
96567d5d AL |
273 | if (err) |
274 | return err; | |
275 | ||
f8b8b1cd | 276 | devlink_port_type_eth_set(&port->devlink_port, port->slave); |
96567d5d | 277 | |
83c0afae AL |
278 | return 0; |
279 | } | |
280 | ||
e41c1b50 | 281 | static void dsa_user_port_unapply(struct dsa_port *port) |
83c0afae | 282 | { |
e41c1b50 | 283 | devlink_port_unregister(&port->devlink_port); |
f8b8b1cd VD |
284 | if (port->slave) { |
285 | dsa_slave_destroy(port->slave); | |
286 | port->slave = NULL; | |
83c0afae AL |
287 | } |
288 | } | |
289 | ||
290 | static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) | |
291 | { | |
293784a8 | 292 | struct dsa_port *port; |
83c0afae AL |
293 | u32 index; |
294 | int err; | |
295 | ||
6e830d8f | 296 | /* Initialize ds->phys_mii_mask before registering the slave MDIO bus |
9d490b4e | 297 | * driver and before ops->setup() has run, since the switch drivers and |
6e830d8f FF |
298 | * the slave MDIO bus driver rely on these values for probing PHY |
299 | * devices or not | |
300 | */ | |
02bc6e54 | 301 | ds->phys_mii_mask |= dsa_user_ports(ds); |
6e830d8f | 302 | |
96567d5d AL |
303 | /* Add the switch to devlink before calling setup, so that setup can |
304 | * add dpipe tables | |
305 | */ | |
306 | ds->devlink = devlink_alloc(&dsa_devlink_ops, 0); | |
307 | if (!ds->devlink) | |
308 | return -ENOMEM; | |
309 | ||
310 | err = devlink_register(ds->devlink, ds->dev); | |
311 | if (err) | |
312 | return err; | |
313 | ||
9d490b4e | 314 | err = ds->ops->setup(ds); |
83c0afae AL |
315 | if (err < 0) |
316 | return err; | |
317 | ||
f515f192 VD |
318 | err = dsa_switch_register_notifier(ds); |
319 | if (err) | |
320 | return err; | |
321 | ||
9d490b4e | 322 | if (!ds->slave_mii_bus && ds->ops->phy_read) { |
1eb59443 FF |
323 | ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); |
324 | if (!ds->slave_mii_bus) | |
325 | return -ENOMEM; | |
326 | ||
327 | dsa_slave_mii_bus_init(ds); | |
328 | ||
329 | err = mdiobus_register(ds->slave_mii_bus); | |
330 | if (err < 0) | |
331 | return err; | |
332 | } | |
333 | ||
26895e29 | 334 | for (index = 0; index < ds->num_ports; index++) { |
293784a8 FF |
335 | port = &ds->ports[index]; |
336 | if (!dsa_port_is_valid(port)) | |
83c0afae AL |
337 | continue; |
338 | ||
339 | if (dsa_port_is_dsa(port)) { | |
e41c1b50 | 340 | err = dsa_dsa_port_apply(port); |
83c0afae AL |
341 | if (err) |
342 | return err; | |
343 | continue; | |
344 | } | |
345 | ||
346 | if (dsa_port_is_cpu(port)) { | |
e41c1b50 | 347 | err = dsa_cpu_port_apply(port); |
83c0afae AL |
348 | if (err) |
349 | return err; | |
350 | continue; | |
351 | } | |
352 | ||
e41c1b50 | 353 | err = dsa_user_port_apply(port); |
83c0afae AL |
354 | if (err) |
355 | continue; | |
356 | } | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds) | |
362 | { | |
293784a8 | 363 | struct dsa_port *port; |
83c0afae AL |
364 | u32 index; |
365 | ||
26895e29 | 366 | for (index = 0; index < ds->num_ports; index++) { |
293784a8 FF |
367 | port = &ds->ports[index]; |
368 | if (!dsa_port_is_valid(port)) | |
83c0afae AL |
369 | continue; |
370 | ||
371 | if (dsa_port_is_dsa(port)) { | |
e41c1b50 | 372 | dsa_dsa_port_unapply(port); |
83c0afae AL |
373 | continue; |
374 | } | |
375 | ||
376 | if (dsa_port_is_cpu(port)) { | |
e41c1b50 | 377 | dsa_cpu_port_unapply(port); |
83c0afae AL |
378 | continue; |
379 | } | |
380 | ||
e41c1b50 | 381 | dsa_user_port_unapply(port); |
83c0afae | 382 | } |
1eb59443 | 383 | |
9d490b4e | 384 | if (ds->slave_mii_bus && ds->ops->phy_read) |
1eb59443 | 385 | mdiobus_unregister(ds->slave_mii_bus); |
f515f192 VD |
386 | |
387 | dsa_switch_unregister_notifier(ds); | |
96567d5d AL |
388 | |
389 | if (ds->devlink) { | |
390 | devlink_unregister(ds->devlink); | |
391 | devlink_free(ds->devlink); | |
392 | ds->devlink = NULL; | |
393 | } | |
394 | ||
83c0afae AL |
395 | } |
396 | ||
397 | static int dsa_dst_apply(struct dsa_switch_tree *dst) | |
398 | { | |
399 | struct dsa_switch *ds; | |
400 | u32 index; | |
401 | int err; | |
402 | ||
403 | for (index = 0; index < DSA_MAX_SWITCHES; index++) { | |
404 | ds = dst->ds[index]; | |
405 | if (!ds) | |
406 | continue; | |
407 | ||
408 | err = dsa_ds_apply(dst, ds); | |
409 | if (err) | |
410 | return err; | |
411 | } | |
412 | ||
413 | /* If we use a tagging format that doesn't have an ethertype | |
414 | * field, make sure that all packets from this point on get | |
415 | * sent to the tag format's receive function. | |
416 | */ | |
417 | wmb(); | |
f8b8b1cd | 418 | dst->cpu_dp->master->dsa_ptr = dst->cpu_dp; |
1943563d | 419 | |
f8b8b1cd | 420 | err = dsa_master_ethtool_setup(dst->cpu_dp->master); |
1943563d VD |
421 | if (err) |
422 | return err; | |
423 | ||
83c0afae AL |
424 | dst->applied = true; |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | static void dsa_dst_unapply(struct dsa_switch_tree *dst) | |
430 | { | |
431 | struct dsa_switch *ds; | |
432 | u32 index; | |
433 | ||
434 | if (!dst->applied) | |
435 | return; | |
436 | ||
f8b8b1cd | 437 | dsa_master_ethtool_restore(dst->cpu_dp->master); |
1943563d | 438 | |
f8b8b1cd | 439 | dst->cpu_dp->master->dsa_ptr = NULL; |
83c0afae AL |
440 | |
441 | /* If we used a tagging format that doesn't have an ethertype | |
442 | * field, make sure that all packets from this point get sent | |
443 | * without the tag and go through the regular receive path. | |
444 | */ | |
445 | wmb(); | |
446 | ||
447 | for (index = 0; index < DSA_MAX_SWITCHES; index++) { | |
448 | ds = dst->ds[index]; | |
449 | if (!ds) | |
450 | continue; | |
451 | ||
452 | dsa_ds_unapply(dst, ds); | |
453 | } | |
454 | ||
cd8d7dd4 | 455 | dst->cpu_dp = NULL; |
0c73c523 | 456 | |
49463b7f | 457 | pr_info("DSA: tree %d unapplied\n", dst->index); |
83c0afae AL |
458 | dst->applied = false; |
459 | } | |
460 | ||
293784a8 | 461 | static int dsa_cpu_parse(struct dsa_port *port, u32 index, |
83c0afae AL |
462 | struct dsa_switch_tree *dst, |
463 | struct dsa_switch *ds) | |
464 | { | |
62fc9587 | 465 | const struct dsa_device_ops *tag_ops; |
7b314362 | 466 | enum dsa_tag_protocol tag_protocol; |
83c0afae | 467 | |
cbabb0ac | 468 | if (!dst->cpu_dp) |
8b0d3ea5 | 469 | dst->cpu_dp = port; |
83c0afae | 470 | |
9d490b4e | 471 | tag_protocol = ds->ops->get_tag_protocol(ds); |
62fc9587 VD |
472 | tag_ops = dsa_resolve_tag_protocol(tag_protocol); |
473 | if (IS_ERR(tag_ops)) { | |
83c0afae | 474 | dev_warn(ds->dev, "No tagger for this switch\n"); |
62fc9587 | 475 | return PTR_ERR(tag_ops); |
83c0afae AL |
476 | } |
477 | ||
15240248 | 478 | dst->cpu_dp->tag_ops = tag_ops; |
3e41f93b VD |
479 | |
480 | /* Make a few copies for faster access in master receive hot path */ | |
481 | dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; | |
3e41f93b | 482 | dst->cpu_dp->dst = dst; |
83c0afae AL |
483 | |
484 | return 0; | |
485 | } | |
486 | ||
487 | static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) | |
488 | { | |
293784a8 | 489 | struct dsa_port *port; |
83c0afae AL |
490 | u32 index; |
491 | int err; | |
492 | ||
26895e29 | 493 | for (index = 0; index < ds->num_ports; index++) { |
293784a8 | 494 | port = &ds->ports[index]; |
14be36c2 FF |
495 | if (!dsa_port_is_valid(port) || |
496 | dsa_port_is_dsa(port)) | |
83c0afae AL |
497 | continue; |
498 | ||
499 | if (dsa_port_is_cpu(port)) { | |
500 | err = dsa_cpu_parse(port, index, dst, ds); | |
501 | if (err) | |
502 | return err; | |
503 | } | |
14be36c2 | 504 | |
83c0afae AL |
505 | } |
506 | ||
49463b7f | 507 | pr_info("DSA: switch %d %d parsed\n", dst->index, ds->index); |
83c0afae AL |
508 | |
509 | return 0; | |
510 | } | |
511 | ||
512 | static int dsa_dst_parse(struct dsa_switch_tree *dst) | |
513 | { | |
514 | struct dsa_switch *ds; | |
e4b77787 | 515 | struct dsa_port *dp; |
83c0afae | 516 | u32 index; |
e4b77787 | 517 | int port; |
83c0afae AL |
518 | int err; |
519 | ||
520 | for (index = 0; index < DSA_MAX_SWITCHES; index++) { | |
521 | ds = dst->ds[index]; | |
522 | if (!ds) | |
523 | continue; | |
524 | ||
525 | err = dsa_ds_parse(dst, ds); | |
526 | if (err) | |
527 | return err; | |
528 | } | |
529 | ||
c7848399 | 530 | if (!dst->cpu_dp) { |
83c0afae AL |
531 | pr_warn("Tree has no master device\n"); |
532 | return -EINVAL; | |
533 | } | |
534 | ||
e4b77787 VD |
535 | /* Assign the default CPU port to all ports of the fabric */ |
536 | for (index = 0; index < DSA_MAX_SWITCHES; index++) { | |
537 | ds = dst->ds[index]; | |
538 | if (!ds) | |
539 | continue; | |
540 | ||
541 | for (port = 0; port < ds->num_ports; port++) { | |
542 | dp = &ds->ports[port]; | |
543 | if (!dsa_port_is_valid(dp) || | |
544 | dsa_port_is_dsa(dp) || | |
545 | dsa_port_is_cpu(dp)) | |
546 | continue; | |
547 | ||
548 | dp->cpu_dp = dst->cpu_dp; | |
549 | } | |
550 | } | |
551 | ||
49463b7f | 552 | pr_info("DSA: tree %d parsed\n", dst->index); |
83c0afae AL |
553 | |
554 | return 0; | |
555 | } | |
556 | ||
fd223e2e VD |
557 | static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) |
558 | { | |
6d4e5c57 VD |
559 | struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); |
560 | struct device_node *link = of_parse_phandle(dn, "link", 0); | |
1838fa89 | 561 | const char *name = of_get_property(dn, "label", NULL); |
6d4e5c57 VD |
562 | |
563 | if (ethernet) { | |
cbabb0ac VD |
564 | struct net_device *master; |
565 | ||
566 | master = of_find_net_device_by_node(ethernet); | |
567 | if (!master) | |
568 | return -EPROBE_DEFER; | |
569 | ||
6d4e5c57 | 570 | dp->type = DSA_PORT_TYPE_CPU; |
cbabb0ac | 571 | dp->master = master; |
6d4e5c57 VD |
572 | } else if (link) { |
573 | dp->type = DSA_PORT_TYPE_DSA; | |
574 | } else { | |
1838fa89 VD |
575 | if (!name) |
576 | name = "eth%d"; | |
577 | ||
6d4e5c57 | 578 | dp->type = DSA_PORT_TYPE_USER; |
1838fa89 | 579 | dp->name = name; |
6d4e5c57 VD |
580 | } |
581 | ||
fd223e2e VD |
582 | dp->dn = dn; |
583 | ||
584 | return 0; | |
585 | } | |
586 | ||
5b32fe07 | 587 | static int dsa_parse_ports_of(struct device_node *dn, struct dsa_switch *ds) |
83c0afae | 588 | { |
5b32fe07 | 589 | struct device_node *ports, *port; |
fd223e2e | 590 | struct dsa_port *dp; |
83c0afae | 591 | u32 reg; |
5b32fe07 VD |
592 | int err; |
593 | ||
594 | ports = of_get_child_by_name(dn, "ports"); | |
595 | if (!ports) { | |
596 | dev_err(ds->dev, "no ports child node found\n"); | |
597 | return -EINVAL; | |
598 | } | |
83c0afae AL |
599 | |
600 | for_each_available_child_of_node(ports, port) { | |
601 | err = of_property_read_u32(port, "reg", ®); | |
602 | if (err) | |
603 | return err; | |
604 | ||
26895e29 | 605 | if (reg >= ds->num_ports) |
83c0afae AL |
606 | return -EINVAL; |
607 | ||
fd223e2e VD |
608 | dp = &ds->ports[reg]; |
609 | ||
610 | err = dsa_port_parse_of(dp, port); | |
611 | if (err) | |
612 | return err; | |
83c0afae AL |
613 | } |
614 | ||
615 | return 0; | |
616 | } | |
617 | ||
fd223e2e VD |
618 | static int dsa_port_parse(struct dsa_port *dp, const char *name, |
619 | struct device *dev) | |
620 | { | |
6d4e5c57 | 621 | if (!strcmp(name, "cpu")) { |
cbabb0ac VD |
622 | struct net_device *master; |
623 | ||
624 | master = dsa_dev_to_net_device(dev); | |
625 | if (!master) | |
626 | return -EPROBE_DEFER; | |
627 | ||
628 | dev_put(master); | |
629 | ||
6d4e5c57 | 630 | dp->type = DSA_PORT_TYPE_CPU; |
cbabb0ac | 631 | dp->master = master; |
6d4e5c57 VD |
632 | } else if (!strcmp(name, "dsa")) { |
633 | dp->type = DSA_PORT_TYPE_DSA; | |
634 | } else { | |
635 | dp->type = DSA_PORT_TYPE_USER; | |
636 | } | |
637 | ||
fd223e2e VD |
638 | dp->name = name; |
639 | ||
640 | return 0; | |
641 | } | |
642 | ||
71e0bbde FF |
643 | static int dsa_parse_ports(struct dsa_chip_data *cd, struct dsa_switch *ds) |
644 | { | |
645 | bool valid_name_found = false; | |
fd223e2e VD |
646 | struct dsa_port *dp; |
647 | struct device *dev; | |
648 | const char *name; | |
71e0bbde | 649 | unsigned int i; |
fd223e2e | 650 | int err; |
71e0bbde FF |
651 | |
652 | for (i = 0; i < DSA_MAX_PORTS; i++) { | |
fd223e2e VD |
653 | name = cd->port_names[i]; |
654 | dev = cd->netdev[i]; | |
655 | dp = &ds->ports[i]; | |
656 | ||
657 | if (!name) | |
71e0bbde FF |
658 | continue; |
659 | ||
fd223e2e VD |
660 | err = dsa_port_parse(dp, name, dev); |
661 | if (err) | |
662 | return err; | |
663 | ||
71e0bbde FF |
664 | valid_name_found = true; |
665 | } | |
666 | ||
667 | if (!valid_name_found && i == DSA_MAX_PORTS) | |
668 | return -EINVAL; | |
669 | ||
670 | return 0; | |
671 | } | |
672 | ||
3512a8e9 | 673 | static int dsa_parse_member_dn(struct device_node *np, u32 *tree, u32 *index) |
83c0afae AL |
674 | { |
675 | int err; | |
676 | ||
677 | *tree = *index = 0; | |
678 | ||
679 | err = of_property_read_u32_index(np, "dsa,member", 0, tree); | |
680 | if (err) { | |
681 | /* Does not exist, but it is optional */ | |
682 | if (err == -EINVAL) | |
683 | return 0; | |
684 | return err; | |
685 | } | |
686 | ||
687 | err = of_property_read_u32_index(np, "dsa,member", 1, index); | |
688 | if (err) | |
689 | return err; | |
690 | ||
691 | if (*index >= DSA_MAX_SWITCHES) | |
692 | return -EINVAL; | |
693 | ||
694 | return 0; | |
695 | } | |
696 | ||
71e0bbde FF |
697 | static int dsa_parse_member(struct dsa_chip_data *pd, u32 *tree, u32 *index) |
698 | { | |
699 | if (!pd) | |
700 | return -ENODEV; | |
701 | ||
702 | /* We do not support complex trees with dsa_chip_data */ | |
703 | *tree = 0; | |
704 | *index = 0; | |
705 | ||
706 | return 0; | |
707 | } | |
708 | ||
23c9ee49 | 709 | static int _dsa_register_switch(struct dsa_switch *ds) |
83c0afae | 710 | { |
23c9ee49 VD |
711 | struct dsa_chip_data *pdata = ds->dev->platform_data; |
712 | struct device_node *np = ds->dev->of_node; | |
83c0afae AL |
713 | struct dsa_switch_tree *dst; |
714 | u32 tree, index; | |
d390238c | 715 | int i, err; |
83c0afae | 716 | |
71e0bbde FF |
717 | if (np) { |
718 | err = dsa_parse_member_dn(np, &tree, &index); | |
719 | if (err) | |
720 | return err; | |
83c0afae | 721 | |
5b32fe07 | 722 | err = dsa_parse_ports_of(np, ds); |
71e0bbde FF |
723 | if (err) |
724 | return err; | |
725 | } else { | |
726 | err = dsa_parse_member(pdata, &tree, &index); | |
727 | if (err) | |
728 | return err; | |
729 | ||
730 | err = dsa_parse_ports(pdata, ds); | |
731 | if (err) | |
732 | return err; | |
733 | } | |
83c0afae AL |
734 | |
735 | dst = dsa_get_dst(tree); | |
736 | if (!dst) { | |
737 | dst = dsa_add_dst(tree); | |
738 | if (!dst) | |
739 | return -ENOMEM; | |
740 | } | |
741 | ||
742 | if (dst->ds[index]) { | |
743 | err = -EBUSY; | |
744 | goto out; | |
745 | } | |
746 | ||
747 | ds->dst = dst; | |
748 | ds->index = index; | |
71e0bbde | 749 | ds->cd = pdata; |
d390238c VD |
750 | |
751 | /* Initialize the routing table */ | |
752 | for (i = 0; i < DSA_MAX_SWITCHES; ++i) | |
753 | ds->rtable[i] = DSA_RTABLE_NONE; | |
754 | ||
83c0afae AL |
755 | dsa_dst_add_ds(dst, ds, index); |
756 | ||
757 | err = dsa_dst_complete(dst); | |
758 | if (err < 0) | |
759 | goto out_del_dst; | |
760 | ||
761 | if (err == 1) { | |
762 | /* Not all switches registered yet */ | |
763 | err = 0; | |
764 | goto out; | |
765 | } | |
766 | ||
767 | if (dst->applied) { | |
768 | pr_info("DSA: Disjoint trees?\n"); | |
769 | return -EINVAL; | |
770 | } | |
771 | ||
772 | err = dsa_dst_parse(dst); | |
cbabb0ac | 773 | if (err) |
83c0afae AL |
774 | goto out_del_dst; |
775 | ||
776 | err = dsa_dst_apply(dst); | |
777 | if (err) { | |
778 | dsa_dst_unapply(dst); | |
779 | goto out_del_dst; | |
780 | } | |
781 | ||
782 | dsa_put_dst(dst); | |
783 | return 0; | |
784 | ||
785 | out_del_dst: | |
786 | dsa_dst_del_ds(dst, ds, ds->index); | |
787 | out: | |
788 | dsa_put_dst(dst); | |
789 | ||
790 | return err; | |
791 | } | |
792 | ||
a0c02161 VD |
793 | struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) |
794 | { | |
795 | size_t size = sizeof(struct dsa_switch) + n * sizeof(struct dsa_port); | |
796 | struct dsa_switch *ds; | |
818be848 | 797 | int i; |
a0c02161 VD |
798 | |
799 | ds = devm_kzalloc(dev, size, GFP_KERNEL); | |
800 | if (!ds) | |
801 | return NULL; | |
802 | ||
803 | ds->dev = dev; | |
804 | ds->num_ports = n; | |
805 | ||
818be848 VD |
806 | for (i = 0; i < ds->num_ports; ++i) { |
807 | ds->ports[i].index = i; | |
808 | ds->ports[i].ds = ds; | |
809 | } | |
810 | ||
a0c02161 VD |
811 | return ds; |
812 | } | |
813 | EXPORT_SYMBOL_GPL(dsa_switch_alloc); | |
814 | ||
23c9ee49 | 815 | int dsa_register_switch(struct dsa_switch *ds) |
83c0afae AL |
816 | { |
817 | int err; | |
818 | ||
819 | mutex_lock(&dsa2_mutex); | |
23c9ee49 | 820 | err = _dsa_register_switch(ds); |
83c0afae AL |
821 | mutex_unlock(&dsa2_mutex); |
822 | ||
823 | return err; | |
824 | } | |
825 | EXPORT_SYMBOL_GPL(dsa_register_switch); | |
826 | ||
85c22bad | 827 | static void _dsa_unregister_switch(struct dsa_switch *ds) |
83c0afae AL |
828 | { |
829 | struct dsa_switch_tree *dst = ds->dst; | |
830 | ||
831 | dsa_dst_unapply(dst); | |
832 | ||
833 | dsa_dst_del_ds(dst, ds, ds->index); | |
834 | } | |
835 | ||
836 | void dsa_unregister_switch(struct dsa_switch *ds) | |
837 | { | |
838 | mutex_lock(&dsa2_mutex); | |
839 | _dsa_unregister_switch(ds); | |
840 | mutex_unlock(&dsa2_mutex); | |
841 | } | |
842 | EXPORT_SYMBOL_GPL(dsa_unregister_switch); |