]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
83c0afae AL |
2 | /* |
3 | * net/dsa/dsa2.c - Hardware switch handling, binding version 2 | |
4 | * Copyright (c) 2008-2009 Marvell Semiconductor | |
5 | * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org> | |
6 | * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> | |
83c0afae AL |
7 | */ |
8 | ||
9 | #include <linux/device.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/list.h> | |
c6e970a0 | 12 | #include <linux/netdevice.h> |
83c0afae AL |
13 | #include <linux/slab.h> |
14 | #include <linux/rtnetlink.h> | |
83c0afae AL |
15 | #include <linux/of.h> |
16 | #include <linux/of_net.h> | |
402f99e5 | 17 | #include <net/devlink.h> |
ea5dd34b | 18 | |
83c0afae AL |
19 | #include "dsa_priv.h" |
20 | ||
83c0afae | 21 | static DEFINE_MUTEX(dsa2_mutex); |
bff33f7e | 22 | LIST_HEAD(dsa_tree_list); |
83c0afae | 23 | |
96567d5d AL |
24 | static const struct devlink_ops dsa_devlink_ops = { |
25 | }; | |
26 | ||
1ca28ec9 | 27 | static struct dsa_switch_tree *dsa_tree_find(int index) |
83c0afae AL |
28 | { |
29 | struct dsa_switch_tree *dst; | |
30 | ||
1ca28ec9 | 31 | list_for_each_entry(dst, &dsa_tree_list, list) |
8e5bf975 | 32 | if (dst->index == index) |
83c0afae | 33 | return dst; |
8e5bf975 | 34 | |
83c0afae AL |
35 | return NULL; |
36 | } | |
37 | ||
1ca28ec9 | 38 | static struct dsa_switch_tree *dsa_tree_alloc(int index) |
83c0afae AL |
39 | { |
40 | struct dsa_switch_tree *dst; | |
41 | ||
42 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | |
43 | if (!dst) | |
44 | return NULL; | |
1ca28ec9 | 45 | |
49463b7f | 46 | dst->index = index; |
1ca28ec9 | 47 | |
c5f51765 VD |
48 | INIT_LIST_HEAD(&dst->rtable); |
49 | ||
ab8ccae1 VD |
50 | INIT_LIST_HEAD(&dst->ports); |
51 | ||
83c0afae | 52 | INIT_LIST_HEAD(&dst->list); |
50c7d2ba | 53 | list_add_tail(&dst->list, &dsa_tree_list); |
8e5bf975 | 54 | |
83c0afae AL |
55 | kref_init(&dst->refcount); |
56 | ||
57 | return dst; | |
58 | } | |
59 | ||
65254108 VD |
60 | static void dsa_tree_free(struct dsa_switch_tree *dst) |
61 | { | |
62 | list_del(&dst->list); | |
63 | kfree(dst); | |
64 | } | |
65 | ||
9e741045 | 66 | static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst) |
1ca28ec9 | 67 | { |
9e741045 VD |
68 | if (dst) |
69 | kref_get(&dst->refcount); | |
1ca28ec9 VD |
70 | |
71 | return dst; | |
72 | } | |
73 | ||
9e741045 | 74 | static struct dsa_switch_tree *dsa_tree_touch(int index) |
65254108 | 75 | { |
9e741045 VD |
76 | struct dsa_switch_tree *dst; |
77 | ||
78 | dst = dsa_tree_find(index); | |
79 | if (dst) | |
80 | return dsa_tree_get(dst); | |
81 | else | |
82 | return dsa_tree_alloc(index); | |
65254108 VD |
83 | } |
84 | ||
85 | static void dsa_tree_release(struct kref *ref) | |
86 | { | |
87 | struct dsa_switch_tree *dst; | |
88 | ||
89 | dst = container_of(ref, struct dsa_switch_tree, refcount); | |
90 | ||
91 | dsa_tree_free(dst); | |
92 | } | |
93 | ||
94 | static void dsa_tree_put(struct dsa_switch_tree *dst) | |
95 | { | |
9e741045 VD |
96 | if (dst) |
97 | kref_put(&dst->refcount, dsa_tree_release); | |
65254108 VD |
98 | } |
99 | ||
293784a8 | 100 | static bool dsa_port_is_dsa(struct dsa_port *port) |
83c0afae | 101 | { |
6d4e5c57 | 102 | return port->type == DSA_PORT_TYPE_DSA; |
293784a8 FF |
103 | } |
104 | ||
105 | static bool dsa_port_is_cpu(struct dsa_port *port) | |
106 | { | |
6d4e5c57 | 107 | return port->type == DSA_PORT_TYPE_CPU; |
83c0afae AL |
108 | } |
109 | ||
f070464c VD |
110 | static bool dsa_port_is_user(struct dsa_port *dp) |
111 | { | |
112 | return dp->type == DSA_PORT_TYPE_USER; | |
113 | } | |
114 | ||
f163da88 VD |
115 | static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, |
116 | struct device_node *dn) | |
83c0afae | 117 | { |
f163da88 | 118 | struct dsa_port *dp; |
83c0afae | 119 | |
764b7e62 VD |
120 | list_for_each_entry(dp, &dst->ports, list) |
121 | if (dp->dn == dn) | |
122 | return dp; | |
83c0afae AL |
123 | |
124 | return NULL; | |
125 | } | |
126 | ||
4e2ce6e5 BDC |
127 | static struct dsa_link *dsa_link_touch(struct dsa_port *dp, |
128 | struct dsa_port *link_dp) | |
c5f51765 VD |
129 | { |
130 | struct dsa_switch *ds = dp->ds; | |
131 | struct dsa_switch_tree *dst; | |
132 | struct dsa_link *dl; | |
133 | ||
134 | dst = ds->dst; | |
135 | ||
136 | list_for_each_entry(dl, &dst->rtable, list) | |
137 | if (dl->dp == dp && dl->link_dp == link_dp) | |
138 | return dl; | |
139 | ||
140 | dl = kzalloc(sizeof(*dl), GFP_KERNEL); | |
141 | if (!dl) | |
142 | return NULL; | |
143 | ||
144 | dl->dp = dp; | |
145 | dl->link_dp = link_dp; | |
146 | ||
147 | INIT_LIST_HEAD(&dl->list); | |
148 | list_add_tail(&dl->list, &dst->rtable); | |
149 | ||
150 | return dl; | |
151 | } | |
152 | ||
34c09a89 | 153 | static bool dsa_port_setup_routing_table(struct dsa_port *dp) |
83c0afae | 154 | { |
34c09a89 VD |
155 | struct dsa_switch *ds = dp->ds; |
156 | struct dsa_switch_tree *dst = ds->dst; | |
157 | struct device_node *dn = dp->dn; | |
c5286665 | 158 | struct of_phandle_iterator it; |
f163da88 | 159 | struct dsa_port *link_dp; |
c5f51765 | 160 | struct dsa_link *dl; |
c5286665 | 161 | int err; |
83c0afae | 162 | |
c5286665 VD |
163 | of_for_each_phandle(&it, err, dn, "link", NULL, 0) { |
164 | link_dp = dsa_tree_find_port_by_node(dst, it.node); | |
165 | if (!link_dp) { | |
166 | of_node_put(it.node); | |
34c09a89 | 167 | return false; |
c5286665 | 168 | } |
83c0afae | 169 | |
c5f51765 VD |
170 | dl = dsa_link_touch(dp, link_dp); |
171 | if (!dl) { | |
172 | of_node_put(it.node); | |
173 | return false; | |
174 | } | |
83c0afae AL |
175 | } |
176 | ||
34c09a89 | 177 | return true; |
83c0afae AL |
178 | } |
179 | ||
3774ecdb | 180 | static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) |
83c0afae | 181 | { |
34c09a89 VD |
182 | bool complete = true; |
183 | struct dsa_port *dp; | |
83c0afae | 184 | |
86bfb2c1 | 185 | list_for_each_entry(dp, &dst->ports, list) { |
3774ecdb | 186 | if (dsa_port_is_dsa(dp)) { |
34c09a89 VD |
187 | complete = dsa_port_setup_routing_table(dp); |
188 | if (!complete) | |
189 | break; | |
190 | } | |
83c0afae AL |
191 | } |
192 | ||
34c09a89 | 193 | return complete; |
83c0afae AL |
194 | } |
195 | ||
f070464c VD |
196 | static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) |
197 | { | |
f070464c | 198 | struct dsa_port *dp; |
f070464c | 199 | |
c0b73628 VD |
200 | list_for_each_entry(dp, &dst->ports, list) |
201 | if (dsa_port_is_cpu(dp)) | |
202 | return dp; | |
f070464c VD |
203 | |
204 | return NULL; | |
205 | } | |
206 | ||
207 | static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) | |
208 | { | |
da4561cd | 209 | struct dsa_port *cpu_dp, *dp; |
f070464c | 210 | |
da4561cd VD |
211 | cpu_dp = dsa_tree_find_first_cpu(dst); |
212 | if (!cpu_dp) { | |
213 | pr_err("DSA: tree %d has no CPU port\n", dst->index); | |
f070464c VD |
214 | return -EINVAL; |
215 | } | |
216 | ||
217 | /* Assign the default CPU port to all ports of the fabric */ | |
da4561cd VD |
218 | list_for_each_entry(dp, &dst->ports, list) |
219 | if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) | |
220 | dp->cpu_dp = cpu_dp; | |
f070464c VD |
221 | |
222 | return 0; | |
223 | } | |
224 | ||
225 | static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) | |
226 | { | |
da4561cd VD |
227 | struct dsa_port *dp; |
228 | ||
229 | list_for_each_entry(dp, &dst->ports, list) | |
230 | if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) | |
231 | dp->cpu_dp = NULL; | |
f070464c VD |
232 | } |
233 | ||
1d27732f | 234 | static int dsa_port_setup(struct dsa_port *dp) |
83c0afae | 235 | { |
1d27732f | 236 | struct dsa_switch *ds = dp->ds; |
15b04ace | 237 | struct dsa_switch_tree *dst = ds->dst; |
955222ca VD |
238 | const unsigned char *id = (const unsigned char *)&dst->index; |
239 | const unsigned char len = sizeof(dst->index); | |
240 | struct devlink_port *dlp = &dp->devlink_port; | |
4ba0ebbc VO |
241 | bool dsa_port_link_registered = false; |
242 | bool devlink_port_registered = false; | |
955222ca | 243 | struct devlink *dl = ds->devlink; |
4ba0ebbc VO |
244 | bool dsa_port_enabled = false; |
245 | int err = 0; | |
83c0afae | 246 | |
fb35c60c VD |
247 | if (dp->setup) |
248 | return 0; | |
249 | ||
1d27732f VD |
250 | switch (dp->type) { |
251 | case DSA_PORT_TYPE_UNUSED: | |
0394a63a | 252 | dsa_port_disable(dp); |
1d27732f VD |
253 | break; |
254 | case DSA_PORT_TYPE_CPU: | |
955222ca VD |
255 | memset(dlp, 0, sizeof(*dlp)); |
256 | devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_CPU, | |
257 | dp->index, false, 0, id, len); | |
258 | err = devlink_port_register(dl, dlp, dp->index); | |
259 | if (err) | |
4ba0ebbc VO |
260 | break; |
261 | devlink_port_registered = true; | |
955222ca | 262 | |
da077392 | 263 | err = dsa_port_link_register_of(dp); |
0394a63a | 264 | if (err) |
4ba0ebbc VO |
265 | break; |
266 | dsa_port_link_registered = true; | |
0394a63a VD |
267 | |
268 | err = dsa_port_enable(dp, NULL); | |
e70c7aad | 269 | if (err) |
4ba0ebbc VO |
270 | break; |
271 | dsa_port_enabled = true; | |
272 | ||
da077392 | 273 | break; |
1d27732f | 274 | case DSA_PORT_TYPE_DSA: |
955222ca VD |
275 | memset(dlp, 0, sizeof(*dlp)); |
276 | devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_DSA, | |
277 | dp->index, false, 0, id, len); | |
278 | err = devlink_port_register(dl, dlp, dp->index); | |
279 | if (err) | |
4ba0ebbc VO |
280 | break; |
281 | devlink_port_registered = true; | |
955222ca | 282 | |
33615367 | 283 | err = dsa_port_link_register_of(dp); |
0394a63a | 284 | if (err) |
4ba0ebbc VO |
285 | break; |
286 | dsa_port_link_registered = true; | |
0394a63a VD |
287 | |
288 | err = dsa_port_enable(dp, NULL); | |
e70c7aad | 289 | if (err) |
4ba0ebbc VO |
290 | break; |
291 | dsa_port_enabled = true; | |
292 | ||
1d27732f VD |
293 | break; |
294 | case DSA_PORT_TYPE_USER: | |
955222ca VD |
295 | memset(dlp, 0, sizeof(*dlp)); |
296 | devlink_port_attrs_set(dlp, DEVLINK_PORT_FLAVOUR_PHYSICAL, | |
297 | dp->index, false, 0, id, len); | |
298 | err = devlink_port_register(dl, dlp, dp->index); | |
299 | if (err) | |
4ba0ebbc VO |
300 | break; |
301 | devlink_port_registered = true; | |
955222ca VD |
302 | |
303 | dp->mac = of_get_mac_address(dp->dn); | |
1d27732f VD |
304 | err = dsa_slave_create(dp); |
305 | if (err) | |
4ba0ebbc | 306 | break; |
955222ca VD |
307 | |
308 | devlink_port_type_eth_set(dlp, dp->slave); | |
1d27732f | 309 | break; |
83c0afae AL |
310 | } |
311 | ||
4ba0ebbc VO |
312 | if (err && dsa_port_enabled) |
313 | dsa_port_disable(dp); | |
314 | if (err && dsa_port_link_registered) | |
315 | dsa_port_link_unregister_of(dp); | |
316 | if (err && devlink_port_registered) | |
317 | devlink_port_unregister(dlp); | |
fb35c60c VD |
318 | if (err) |
319 | return err; | |
4ba0ebbc | 320 | |
fb35c60c VD |
321 | dp->setup = true; |
322 | ||
323 | return 0; | |
83c0afae AL |
324 | } |
325 | ||
1d27732f | 326 | static void dsa_port_teardown(struct dsa_port *dp) |
83c0afae | 327 | { |
955222ca | 328 | struct devlink_port *dlp = &dp->devlink_port; |
1d27732f | 329 | |
fb35c60c VD |
330 | if (!dp->setup) |
331 | return; | |
332 | ||
1d27732f VD |
333 | switch (dp->type) { |
334 | case DSA_PORT_TYPE_UNUSED: | |
335 | break; | |
336 | case DSA_PORT_TYPE_CPU: | |
0394a63a | 337 | dsa_port_disable(dp); |
4dad81ee | 338 | dsa_tag_driver_put(dp->tag_ops); |
955222ca VD |
339 | devlink_port_unregister(dlp); |
340 | dsa_port_link_unregister_of(dp); | |
341 | break; | |
1d27732f | 342 | case DSA_PORT_TYPE_DSA: |
0394a63a | 343 | dsa_port_disable(dp); |
955222ca | 344 | devlink_port_unregister(dlp); |
33615367 | 345 | dsa_port_link_unregister_of(dp); |
1d27732f VD |
346 | break; |
347 | case DSA_PORT_TYPE_USER: | |
955222ca | 348 | devlink_port_unregister(dlp); |
1d27732f VD |
349 | if (dp->slave) { |
350 | dsa_slave_destroy(dp->slave); | |
351 | dp->slave = NULL; | |
352 | } | |
353 | break; | |
83c0afae | 354 | } |
fb35c60c VD |
355 | |
356 | dp->setup = false; | |
83c0afae AL |
357 | } |
358 | ||
1f08f9e9 | 359 | static int dsa_switch_setup(struct dsa_switch *ds) |
83c0afae | 360 | { |
6b297524 | 361 | struct dsa_devlink_priv *dl_priv; |
fb35c60c VD |
362 | int err; |
363 | ||
364 | if (ds->setup) | |
365 | return 0; | |
83c0afae | 366 | |
6e830d8f | 367 | /* Initialize ds->phys_mii_mask before registering the slave MDIO bus |
9d490b4e | 368 | * driver and before ops->setup() has run, since the switch drivers and |
6e830d8f FF |
369 | * the slave MDIO bus driver rely on these values for probing PHY |
370 | * devices or not | |
371 | */ | |
02bc6e54 | 372 | ds->phys_mii_mask |= dsa_user_ports(ds); |
6e830d8f | 373 | |
96567d5d AL |
374 | /* Add the switch to devlink before calling setup, so that setup can |
375 | * add dpipe tables | |
376 | */ | |
6b297524 | 377 | ds->devlink = devlink_alloc(&dsa_devlink_ops, sizeof(*dl_priv)); |
96567d5d AL |
378 | if (!ds->devlink) |
379 | return -ENOMEM; | |
6b297524 AL |
380 | dl_priv = devlink_priv(ds->devlink); |
381 | dl_priv->ds = ds; | |
96567d5d AL |
382 | |
383 | err = devlink_register(ds->devlink, ds->dev); | |
384 | if (err) | |
e70c7aad | 385 | goto free_devlink; |
96567d5d | 386 | |
f515f192 VD |
387 | err = dsa_switch_register_notifier(ds); |
388 | if (err) | |
e70c7aad | 389 | goto unregister_devlink; |
f515f192 | 390 | |
b2243b36 VO |
391 | err = ds->ops->setup(ds); |
392 | if (err < 0) | |
e70c7aad | 393 | goto unregister_notifier; |
b2243b36 | 394 | |
6b297524 AL |
395 | devlink_params_publish(ds->devlink); |
396 | ||
9d490b4e | 397 | if (!ds->slave_mii_bus && ds->ops->phy_read) { |
1eb59443 | 398 | ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); |
e70c7aad IC |
399 | if (!ds->slave_mii_bus) { |
400 | err = -ENOMEM; | |
401 | goto unregister_notifier; | |
402 | } | |
1eb59443 FF |
403 | |
404 | dsa_slave_mii_bus_init(ds); | |
405 | ||
406 | err = mdiobus_register(ds->slave_mii_bus); | |
407 | if (err < 0) | |
e70c7aad | 408 | goto unregister_notifier; |
1eb59443 FF |
409 | } |
410 | ||
fb35c60c VD |
411 | ds->setup = true; |
412 | ||
83c0afae | 413 | return 0; |
e70c7aad IC |
414 | |
415 | unregister_notifier: | |
416 | dsa_switch_unregister_notifier(ds); | |
417 | unregister_devlink: | |
418 | devlink_unregister(ds->devlink); | |
419 | free_devlink: | |
420 | devlink_free(ds->devlink); | |
421 | ds->devlink = NULL; | |
422 | ||
423 | return err; | |
83c0afae AL |
424 | } |
425 | ||
1f08f9e9 | 426 | static void dsa_switch_teardown(struct dsa_switch *ds) |
83c0afae | 427 | { |
fb35c60c VD |
428 | if (!ds->setup) |
429 | return; | |
430 | ||
9d490b4e | 431 | if (ds->slave_mii_bus && ds->ops->phy_read) |
1eb59443 | 432 | mdiobus_unregister(ds->slave_mii_bus); |
f515f192 VD |
433 | |
434 | dsa_switch_unregister_notifier(ds); | |
96567d5d | 435 | |
5e3f847a VO |
436 | if (ds->ops->teardown) |
437 | ds->ops->teardown(ds); | |
438 | ||
96567d5d AL |
439 | if (ds->devlink) { |
440 | devlink_unregister(ds->devlink); | |
441 | devlink_free(ds->devlink); | |
442 | ds->devlink = NULL; | |
443 | } | |
444 | ||
fb35c60c | 445 | ds->setup = false; |
83c0afae AL |
446 | } |
447 | ||
1f08f9e9 VD |
448 | static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) |
449 | { | |
1d27732f | 450 | struct dsa_port *dp; |
fb35c60c | 451 | int err; |
1f08f9e9 | 452 | |
fb35c60c VD |
453 | list_for_each_entry(dp, &dst->ports, list) { |
454 | err = dsa_switch_setup(dp->ds); | |
1f08f9e9 | 455 | if (err) |
fb35c60c VD |
456 | goto teardown; |
457 | } | |
1d27732f | 458 | |
fb35c60c VD |
459 | list_for_each_entry(dp, &dst->ports, list) { |
460 | err = dsa_port_setup(dp); | |
461 | if (err) | |
86f8b1c0 | 462 | continue; |
1f08f9e9 VD |
463 | } |
464 | ||
465 | return 0; | |
e70c7aad | 466 | |
fb35c60c VD |
467 | teardown: |
468 | list_for_each_entry(dp, &dst->ports, list) | |
469 | dsa_port_teardown(dp); | |
e70c7aad | 470 | |
fb35c60c VD |
471 | list_for_each_entry(dp, &dst->ports, list) |
472 | dsa_switch_teardown(dp->ds); | |
e70c7aad IC |
473 | |
474 | return err; | |
1f08f9e9 VD |
475 | } |
476 | ||
477 | static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) | |
478 | { | |
1d27732f | 479 | struct dsa_port *dp; |
1d27732f | 480 | |
fb35c60c VD |
481 | list_for_each_entry(dp, &dst->ports, list) |
482 | dsa_port_teardown(dp); | |
1d27732f | 483 | |
fb35c60c VD |
484 | list_for_each_entry(dp, &dst->ports, list) |
485 | dsa_switch_teardown(dp->ds); | |
1f08f9e9 VD |
486 | } |
487 | ||
17a22fcf VD |
488 | static int dsa_tree_setup_master(struct dsa_switch_tree *dst) |
489 | { | |
0cfec588 VD |
490 | struct dsa_port *dp; |
491 | int err; | |
17a22fcf | 492 | |
0cfec588 VD |
493 | list_for_each_entry(dp, &dst->ports, list) { |
494 | if (dsa_port_is_cpu(dp)) { | |
495 | err = dsa_master_setup(dp->master, dp); | |
496 | if (err) | |
497 | return err; | |
498 | } | |
499 | } | |
500 | ||
501 | return 0; | |
17a22fcf VD |
502 | } |
503 | ||
504 | static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) | |
505 | { | |
0cfec588 | 506 | struct dsa_port *dp; |
17a22fcf | 507 | |
0cfec588 VD |
508 | list_for_each_entry(dp, &dst->ports, list) |
509 | if (dsa_port_is_cpu(dp)) | |
510 | dsa_master_teardown(dp->master); | |
17a22fcf VD |
511 | } |
512 | ||
ec15dd42 | 513 | static int dsa_tree_setup(struct dsa_switch_tree *dst) |
83c0afae | 514 | { |
34c09a89 | 515 | bool complete; |
83c0afae AL |
516 | int err; |
517 | ||
ec15dd42 VD |
518 | if (dst->setup) { |
519 | pr_err("DSA: tree %d already setup! Disjoint trees?\n", | |
520 | dst->index); | |
521 | return -EEXIST; | |
522 | } | |
523 | ||
34c09a89 VD |
524 | complete = dsa_tree_setup_routing_table(dst); |
525 | if (!complete) | |
526 | return 0; | |
527 | ||
f070464c VD |
528 | err = dsa_tree_setup_default_cpu(dst); |
529 | if (err) | |
530 | return err; | |
531 | ||
1f08f9e9 VD |
532 | err = dsa_tree_setup_switches(dst); |
533 | if (err) | |
e70c7aad | 534 | goto teardown_default_cpu; |
83c0afae | 535 | |
17a22fcf | 536 | err = dsa_tree_setup_master(dst); |
1943563d | 537 | if (err) |
e70c7aad | 538 | goto teardown_switches; |
1943563d | 539 | |
ec15dd42 VD |
540 | dst->setup = true; |
541 | ||
542 | pr_info("DSA: tree %d setup\n", dst->index); | |
83c0afae AL |
543 | |
544 | return 0; | |
e70c7aad IC |
545 | |
546 | teardown_switches: | |
547 | dsa_tree_teardown_switches(dst); | |
548 | teardown_default_cpu: | |
549 | dsa_tree_teardown_default_cpu(dst); | |
550 | ||
551 | return err; | |
83c0afae AL |
552 | } |
553 | ||
ec15dd42 | 554 | static void dsa_tree_teardown(struct dsa_switch_tree *dst) |
83c0afae | 555 | { |
c5f51765 VD |
556 | struct dsa_link *dl, *next; |
557 | ||
ec15dd42 | 558 | if (!dst->setup) |
83c0afae AL |
559 | return; |
560 | ||
17a22fcf | 561 | dsa_tree_teardown_master(dst); |
83c0afae | 562 | |
1f08f9e9 | 563 | dsa_tree_teardown_switches(dst); |
83c0afae | 564 | |
f070464c | 565 | dsa_tree_teardown_default_cpu(dst); |
0c73c523 | 566 | |
c5f51765 VD |
567 | list_for_each_entry_safe(dl, next, &dst->rtable, list) { |
568 | list_del(&dl->list); | |
569 | kfree(dl); | |
570 | } | |
571 | ||
ec15dd42 VD |
572 | pr_info("DSA: tree %d torn down\n", dst->index); |
573 | ||
574 | dst->setup = false; | |
83c0afae AL |
575 | } |
576 | ||
ab8ccae1 VD |
577 | static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) |
578 | { | |
579 | struct dsa_switch_tree *dst = ds->dst; | |
580 | struct dsa_port *dp; | |
581 | ||
05f294a8 VD |
582 | list_for_each_entry(dp, &dst->ports, list) |
583 | if (dp->ds == ds && dp->index == index) | |
584 | return dp; | |
585 | ||
586 | dp = kzalloc(sizeof(*dp), GFP_KERNEL); | |
587 | if (!dp) | |
588 | return NULL; | |
ab8ccae1 VD |
589 | |
590 | dp->ds = ds; | |
591 | dp->index = index; | |
592 | ||
593 | INIT_LIST_HEAD(&dp->list); | |
594 | list_add_tail(&dp->list, &dst->ports); | |
595 | ||
596 | return dp; | |
597 | } | |
598 | ||
06e24d08 VD |
599 | static int dsa_port_parse_user(struct dsa_port *dp, const char *name) |
600 | { | |
601 | if (!name) | |
602 | name = "eth%d"; | |
603 | ||
604 | dp->type = DSA_PORT_TYPE_USER; | |
605 | dp->name = name; | |
606 | ||
607 | return 0; | |
608 | } | |
609 | ||
610 | static int dsa_port_parse_dsa(struct dsa_port *dp) | |
611 | { | |
612 | dp->type = DSA_PORT_TYPE_DSA; | |
613 | ||
614 | return 0; | |
615 | } | |
616 | ||
4d776482 FF |
617 | static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp, |
618 | struct net_device *master) | |
619 | { | |
620 | enum dsa_tag_protocol tag_protocol = DSA_TAG_PROTO_NONE; | |
621 | struct dsa_switch *mds, *ds = dp->ds; | |
622 | unsigned int mdp_upstream; | |
623 | struct dsa_port *mdp; | |
624 | ||
625 | /* It is possible to stack DSA switches onto one another when that | |
626 | * happens the switch driver may want to know if its tagging protocol | |
627 | * is going to work in such a configuration. | |
628 | */ | |
629 | if (dsa_slave_dev_check(master)) { | |
630 | mdp = dsa_slave_to_port(master); | |
631 | mds = mdp->ds; | |
632 | mdp_upstream = dsa_upstream_port(mds, mdp->index); | |
633 | tag_protocol = mds->ops->get_tag_protocol(mds, mdp_upstream, | |
634 | DSA_TAG_PROTO_NONE); | |
635 | } | |
636 | ||
637 | /* If the master device is not itself a DSA slave in a disjoint DSA | |
638 | * tree, then return immediately. | |
639 | */ | |
640 | return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol); | |
641 | } | |
642 | ||
06e24d08 VD |
643 | static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) |
644 | { | |
7354fcb0 VD |
645 | struct dsa_switch *ds = dp->ds; |
646 | struct dsa_switch_tree *dst = ds->dst; | |
647 | const struct dsa_device_ops *tag_ops; | |
648 | enum dsa_tag_protocol tag_protocol; | |
649 | ||
4d776482 | 650 | tag_protocol = dsa_get_tag_protocol(dp, master); |
c39e2a1d | 651 | tag_ops = dsa_tag_driver_get(tag_protocol); |
7354fcb0 | 652 | if (IS_ERR(tag_ops)) { |
23426a25 AL |
653 | if (PTR_ERR(tag_ops) == -ENOPROTOOPT) |
654 | return -EPROBE_DEFER; | |
7354fcb0 | 655 | dev_warn(ds->dev, "No tagger for this switch\n"); |
4d776482 | 656 | dp->master = NULL; |
7354fcb0 VD |
657 | return PTR_ERR(tag_ops); |
658 | } | |
659 | ||
4d776482 | 660 | dp->master = master; |
06e24d08 | 661 | dp->type = DSA_PORT_TYPE_CPU; |
cc1939e4 | 662 | dp->filter = tag_ops->filter; |
7354fcb0 VD |
663 | dp->rcv = tag_ops->rcv; |
664 | dp->tag_ops = tag_ops; | |
7354fcb0 | 665 | dp->dst = dst; |
06e24d08 VD |
666 | |
667 | return 0; | |
668 | } | |
669 | ||
fd223e2e VD |
670 | static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) |
671 | { | |
6d4e5c57 | 672 | struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); |
1838fa89 | 673 | const char *name = of_get_property(dn, "label", NULL); |
54df6fa9 | 674 | bool link = of_property_read_bool(dn, "link"); |
6d4e5c57 | 675 | |
06e24d08 VD |
676 | dp->dn = dn; |
677 | ||
6d4e5c57 | 678 | if (ethernet) { |
cbabb0ac VD |
679 | struct net_device *master; |
680 | ||
681 | master = of_find_net_device_by_node(ethernet); | |
682 | if (!master) | |
683 | return -EPROBE_DEFER; | |
684 | ||
06e24d08 | 685 | return dsa_port_parse_cpu(dp, master); |
6d4e5c57 VD |
686 | } |
687 | ||
06e24d08 VD |
688 | if (link) |
689 | return dsa_port_parse_dsa(dp); | |
fd223e2e | 690 | |
06e24d08 | 691 | return dsa_port_parse_user(dp, name); |
fd223e2e VD |
692 | } |
693 | ||
975e6e32 VD |
694 | static int dsa_switch_parse_ports_of(struct dsa_switch *ds, |
695 | struct device_node *dn) | |
83c0afae | 696 | { |
5b32fe07 | 697 | struct device_node *ports, *port; |
fd223e2e | 698 | struct dsa_port *dp; |
9919a363 | 699 | int err = 0; |
83c0afae | 700 | u32 reg; |
5b32fe07 VD |
701 | |
702 | ports = of_get_child_by_name(dn, "ports"); | |
703 | if (!ports) { | |
704 | dev_err(ds->dev, "no ports child node found\n"); | |
705 | return -EINVAL; | |
706 | } | |
83c0afae AL |
707 | |
708 | for_each_available_child_of_node(ports, port) { | |
709 | err = of_property_read_u32(port, "reg", ®); | |
710 | if (err) | |
9919a363 | 711 | goto out_put_node; |
83c0afae | 712 | |
9919a363 WY |
713 | if (reg >= ds->num_ports) { |
714 | err = -EINVAL; | |
715 | goto out_put_node; | |
716 | } | |
83c0afae | 717 | |
68bb8ea8 | 718 | dp = dsa_to_port(ds, reg); |
fd223e2e VD |
719 | |
720 | err = dsa_port_parse_of(dp, port); | |
721 | if (err) | |
9919a363 | 722 | goto out_put_node; |
83c0afae AL |
723 | } |
724 | ||
9919a363 WY |
725 | out_put_node: |
726 | of_node_put(ports); | |
727 | return err; | |
83c0afae AL |
728 | } |
729 | ||
975e6e32 VD |
730 | static int dsa_switch_parse_member_of(struct dsa_switch *ds, |
731 | struct device_node *dn) | |
732 | { | |
733 | u32 m[2] = { 0, 0 }; | |
734 | int sz; | |
735 | ||
736 | /* Don't error out if this optional property isn't found */ | |
737 | sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2); | |
738 | if (sz < 0 && sz != -EINVAL) | |
739 | return sz; | |
740 | ||
741 | ds->index = m[1]; | |
975e6e32 VD |
742 | |
743 | ds->dst = dsa_tree_touch(m[0]); | |
744 | if (!ds->dst) | |
745 | return -ENOMEM; | |
746 | ||
747 | return 0; | |
748 | } | |
749 | ||
ab8ccae1 VD |
750 | static int dsa_switch_touch_ports(struct dsa_switch *ds) |
751 | { | |
752 | struct dsa_port *dp; | |
753 | int port; | |
754 | ||
755 | for (port = 0; port < ds->num_ports; port++) { | |
756 | dp = dsa_port_touch(ds, port); | |
757 | if (!dp) | |
758 | return -ENOMEM; | |
759 | } | |
760 | ||
761 | return 0; | |
762 | } | |
763 | ||
975e6e32 VD |
764 | static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) |
765 | { | |
766 | int err; | |
767 | ||
768 | err = dsa_switch_parse_member_of(ds, dn); | |
769 | if (err) | |
770 | return err; | |
771 | ||
ab8ccae1 VD |
772 | err = dsa_switch_touch_ports(ds); |
773 | if (err) | |
774 | return err; | |
775 | ||
975e6e32 VD |
776 | return dsa_switch_parse_ports_of(ds, dn); |
777 | } | |
778 | ||
fd223e2e VD |
779 | static int dsa_port_parse(struct dsa_port *dp, const char *name, |
780 | struct device *dev) | |
781 | { | |
6d4e5c57 | 782 | if (!strcmp(name, "cpu")) { |
cbabb0ac VD |
783 | struct net_device *master; |
784 | ||
785 | master = dsa_dev_to_net_device(dev); | |
786 | if (!master) | |
787 | return -EPROBE_DEFER; | |
788 | ||
789 | dev_put(master); | |
790 | ||
06e24d08 | 791 | return dsa_port_parse_cpu(dp, master); |
6d4e5c57 VD |
792 | } |
793 | ||
06e24d08 VD |
794 | if (!strcmp(name, "dsa")) |
795 | return dsa_port_parse_dsa(dp); | |
fd223e2e | 796 | |
06e24d08 | 797 | return dsa_port_parse_user(dp, name); |
fd223e2e VD |
798 | } |
799 | ||
975e6e32 VD |
800 | static int dsa_switch_parse_ports(struct dsa_switch *ds, |
801 | struct dsa_chip_data *cd) | |
71e0bbde FF |
802 | { |
803 | bool valid_name_found = false; | |
fd223e2e VD |
804 | struct dsa_port *dp; |
805 | struct device *dev; | |
806 | const char *name; | |
71e0bbde | 807 | unsigned int i; |
fd223e2e | 808 | int err; |
71e0bbde FF |
809 | |
810 | for (i = 0; i < DSA_MAX_PORTS; i++) { | |
fd223e2e VD |
811 | name = cd->port_names[i]; |
812 | dev = cd->netdev[i]; | |
68bb8ea8 | 813 | dp = dsa_to_port(ds, i); |
fd223e2e VD |
814 | |
815 | if (!name) | |
71e0bbde FF |
816 | continue; |
817 | ||
fd223e2e VD |
818 | err = dsa_port_parse(dp, name, dev); |
819 | if (err) | |
820 | return err; | |
821 | ||
71e0bbde FF |
822 | valid_name_found = true; |
823 | } | |
824 | ||
825 | if (!valid_name_found && i == DSA_MAX_PORTS) | |
826 | return -EINVAL; | |
827 | ||
828 | return 0; | |
829 | } | |
830 | ||
975e6e32 | 831 | static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) |
71e0bbde | 832 | { |
ab8ccae1 VD |
833 | int err; |
834 | ||
975e6e32 | 835 | ds->cd = cd; |
71e0bbde | 836 | |
975e6e32 VD |
837 | /* We don't support interconnected switches nor multiple trees via |
838 | * platform data, so this is the unique switch of the tree. | |
839 | */ | |
840 | ds->index = 0; | |
841 | ds->dst = dsa_tree_touch(0); | |
842 | if (!ds->dst) | |
843 | return -ENOMEM; | |
71e0bbde | 844 | |
ab8ccae1 VD |
845 | err = dsa_switch_touch_ports(ds); |
846 | if (err) | |
847 | return err; | |
848 | ||
975e6e32 | 849 | return dsa_switch_parse_ports(ds, cd); |
71e0bbde FF |
850 | } |
851 | ||
6dc43cd3 VO |
852 | static void dsa_switch_release_ports(struct dsa_switch *ds) |
853 | { | |
854 | struct dsa_switch_tree *dst = ds->dst; | |
855 | struct dsa_port *dp, *next; | |
856 | ||
857 | list_for_each_entry_safe(dp, next, &dst->ports, list) { | |
858 | if (dp->ds != ds) | |
859 | continue; | |
860 | list_del(&dp->list); | |
861 | kfree(dp); | |
862 | } | |
863 | } | |
864 | ||
b4fbb347 | 865 | static int dsa_switch_probe(struct dsa_switch *ds) |
83c0afae | 866 | { |
8e5cb84c | 867 | struct dsa_switch_tree *dst; |
556f124f CIK |
868 | struct dsa_chip_data *pdata; |
869 | struct device_node *np; | |
34c09a89 | 870 | int err; |
83c0afae | 871 | |
7e99e347 VD |
872 | if (!ds->dev) |
873 | return -ENODEV; | |
874 | ||
556f124f CIK |
875 | pdata = ds->dev->platform_data; |
876 | np = ds->dev->of_node; | |
877 | ||
7e99e347 VD |
878 | if (!ds->num_ports) |
879 | return -EINVAL; | |
880 | ||
6dc43cd3 | 881 | if (np) { |
975e6e32 | 882 | err = dsa_switch_parse_of(ds, np); |
6dc43cd3 VO |
883 | if (err) |
884 | dsa_switch_release_ports(ds); | |
885 | } else if (pdata) { | |
975e6e32 | 886 | err = dsa_switch_parse(ds, pdata); |
6dc43cd3 VO |
887 | if (err) |
888 | dsa_switch_release_ports(ds); | |
889 | } else { | |
975e6e32 | 890 | err = -ENODEV; |
6dc43cd3 | 891 | } |
83c0afae | 892 | |
975e6e32 VD |
893 | if (err) |
894 | return err; | |
d390238c | 895 | |
8e5cb84c VD |
896 | dst = ds->dst; |
897 | dsa_tree_get(dst); | |
898 | err = dsa_tree_setup(dst); | |
6dc43cd3 VO |
899 | if (err) { |
900 | dsa_switch_release_ports(ds); | |
8e5cb84c | 901 | dsa_tree_put(dst); |
6dc43cd3 | 902 | } |
8e5cb84c VD |
903 | |
904 | return err; | |
83c0afae AL |
905 | } |
906 | ||
23c9ee49 | 907 | int dsa_register_switch(struct dsa_switch *ds) |
83c0afae AL |
908 | { |
909 | int err; | |
910 | ||
911 | mutex_lock(&dsa2_mutex); | |
b4fbb347 | 912 | err = dsa_switch_probe(ds); |
9e741045 | 913 | dsa_tree_put(ds->dst); |
83c0afae AL |
914 | mutex_unlock(&dsa2_mutex); |
915 | ||
916 | return err; | |
917 | } | |
918 | EXPORT_SYMBOL_GPL(dsa_register_switch); | |
919 | ||
b4fbb347 | 920 | static void dsa_switch_remove(struct dsa_switch *ds) |
83c0afae AL |
921 | { |
922 | struct dsa_switch_tree *dst = ds->dst; | |
05f294a8 | 923 | |
c058f6df | 924 | dsa_tree_teardown(dst); |
6dc43cd3 | 925 | dsa_switch_release_ports(ds); |
8e5cb84c | 926 | dsa_tree_put(dst); |
83c0afae AL |
927 | } |
928 | ||
929 | void dsa_unregister_switch(struct dsa_switch *ds) | |
930 | { | |
931 | mutex_lock(&dsa2_mutex); | |
b4fbb347 | 932 | dsa_switch_remove(ds); |
83c0afae AL |
933 | mutex_unlock(&dsa2_mutex); |
934 | } | |
935 | EXPORT_SYMBOL_GPL(dsa_unregister_switch); |