]>
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 | ||
1ca28ec9 | 21 | static LIST_HEAD(dsa_tree_list); |
83c0afae AL |
22 | static DEFINE_MUTEX(dsa2_mutex); |
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 | |
83c0afae | 48 | INIT_LIST_HEAD(&dst->list); |
1ca28ec9 | 49 | list_add_tail(&dsa_tree_list, &dst->list); |
8e5bf975 | 50 | |
83c0afae AL |
51 | kref_init(&dst->refcount); |
52 | ||
53 | return dst; | |
54 | } | |
55 | ||
65254108 VD |
56 | static void dsa_tree_free(struct dsa_switch_tree *dst) |
57 | { | |
58 | list_del(&dst->list); | |
59 | kfree(dst); | |
60 | } | |
61 | ||
9e741045 | 62 | static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst) |
1ca28ec9 | 63 | { |
9e741045 VD |
64 | if (dst) |
65 | kref_get(&dst->refcount); | |
1ca28ec9 VD |
66 | |
67 | return dst; | |
68 | } | |
69 | ||
9e741045 | 70 | static struct dsa_switch_tree *dsa_tree_touch(int index) |
65254108 | 71 | { |
9e741045 VD |
72 | struct dsa_switch_tree *dst; |
73 | ||
74 | dst = dsa_tree_find(index); | |
75 | if (dst) | |
76 | return dsa_tree_get(dst); | |
77 | else | |
78 | return dsa_tree_alloc(index); | |
65254108 VD |
79 | } |
80 | ||
81 | static void dsa_tree_release(struct kref *ref) | |
82 | { | |
83 | struct dsa_switch_tree *dst; | |
84 | ||
85 | dst = container_of(ref, struct dsa_switch_tree, refcount); | |
86 | ||
87 | dsa_tree_free(dst); | |
88 | } | |
89 | ||
90 | static void dsa_tree_put(struct dsa_switch_tree *dst) | |
91 | { | |
9e741045 VD |
92 | if (dst) |
93 | kref_put(&dst->refcount, dsa_tree_release); | |
65254108 VD |
94 | } |
95 | ||
293784a8 | 96 | static bool dsa_port_is_dsa(struct dsa_port *port) |
83c0afae | 97 | { |
6d4e5c57 | 98 | return port->type == DSA_PORT_TYPE_DSA; |
293784a8 FF |
99 | } |
100 | ||
101 | static bool dsa_port_is_cpu(struct dsa_port *port) | |
102 | { | |
6d4e5c57 | 103 | return port->type == DSA_PORT_TYPE_CPU; |
83c0afae AL |
104 | } |
105 | ||
f070464c VD |
106 | static bool dsa_port_is_user(struct dsa_port *dp) |
107 | { | |
108 | return dp->type == DSA_PORT_TYPE_USER; | |
109 | } | |
110 | ||
f163da88 VD |
111 | static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, |
112 | struct device_node *dn) | |
83c0afae AL |
113 | { |
114 | struct dsa_switch *ds; | |
f163da88 VD |
115 | struct dsa_port *dp; |
116 | int device, port; | |
83c0afae | 117 | |
f163da88 VD |
118 | for (device = 0; device < DSA_MAX_SWITCHES; device++) { |
119 | ds = dst->ds[device]; | |
83c0afae AL |
120 | if (!ds) |
121 | continue; | |
122 | ||
f163da88 VD |
123 | for (port = 0; port < ds->num_ports; port++) { |
124 | dp = &ds->ports[port]; | |
125 | ||
126 | if (dp->dn == dn) | |
127 | return dp; | |
128 | } | |
83c0afae AL |
129 | } |
130 | ||
131 | return NULL; | |
132 | } | |
133 | ||
34c09a89 | 134 | static bool dsa_port_setup_routing_table(struct dsa_port *dp) |
83c0afae | 135 | { |
34c09a89 VD |
136 | struct dsa_switch *ds = dp->ds; |
137 | struct dsa_switch_tree *dst = ds->dst; | |
138 | struct device_node *dn = dp->dn; | |
c5286665 | 139 | struct of_phandle_iterator it; |
f163da88 | 140 | struct dsa_port *link_dp; |
c5286665 | 141 | int err; |
83c0afae | 142 | |
c5286665 VD |
143 | of_for_each_phandle(&it, err, dn, "link", NULL, 0) { |
144 | link_dp = dsa_tree_find_port_by_node(dst, it.node); | |
145 | if (!link_dp) { | |
146 | of_node_put(it.node); | |
34c09a89 | 147 | return false; |
c5286665 | 148 | } |
83c0afae | 149 | |
34c09a89 | 150 | ds->rtable[link_dp->ds->index] = dp->index; |
83c0afae AL |
151 | } |
152 | ||
34c09a89 | 153 | return true; |
83c0afae AL |
154 | } |
155 | ||
34c09a89 | 156 | static bool dsa_switch_setup_routing_table(struct dsa_switch *ds) |
83c0afae | 157 | { |
34c09a89 VD |
158 | bool complete = true; |
159 | struct dsa_port *dp; | |
160 | int i; | |
83c0afae | 161 | |
34c09a89 VD |
162 | for (i = 0; i < DSA_MAX_SWITCHES; i++) |
163 | ds->rtable[i] = DSA_RTABLE_NONE; | |
83c0afae | 164 | |
34c09a89 VD |
165 | for (i = 0; i < ds->num_ports; i++) { |
166 | dp = &ds->ports[i]; | |
83c0afae | 167 | |
34c09a89 VD |
168 | if (dsa_port_is_dsa(dp)) { |
169 | complete = dsa_port_setup_routing_table(dp); | |
170 | if (!complete) | |
171 | break; | |
172 | } | |
83c0afae AL |
173 | } |
174 | ||
34c09a89 | 175 | return complete; |
83c0afae AL |
176 | } |
177 | ||
34c09a89 | 178 | static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) |
83c0afae AL |
179 | { |
180 | struct dsa_switch *ds; | |
34c09a89 VD |
181 | bool complete = true; |
182 | int device; | |
83c0afae | 183 | |
34c09a89 VD |
184 | for (device = 0; device < DSA_MAX_SWITCHES; device++) { |
185 | ds = dst->ds[device]; | |
83c0afae AL |
186 | if (!ds) |
187 | continue; | |
188 | ||
34c09a89 VD |
189 | complete = dsa_switch_setup_routing_table(ds); |
190 | if (!complete) | |
191 | break; | |
83c0afae AL |
192 | } |
193 | ||
34c09a89 | 194 | return complete; |
83c0afae AL |
195 | } |
196 | ||
f070464c VD |
197 | static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) |
198 | { | |
199 | struct dsa_switch *ds; | |
200 | struct dsa_port *dp; | |
201 | int device, port; | |
202 | ||
203 | for (device = 0; device < DSA_MAX_SWITCHES; device++) { | |
204 | ds = dst->ds[device]; | |
205 | if (!ds) | |
206 | continue; | |
207 | ||
208 | for (port = 0; port < ds->num_ports; port++) { | |
209 | dp = &ds->ports[port]; | |
210 | ||
211 | if (dsa_port_is_cpu(dp)) | |
212 | return dp; | |
213 | } | |
214 | } | |
215 | ||
216 | return NULL; | |
217 | } | |
218 | ||
219 | static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) | |
220 | { | |
221 | struct dsa_switch *ds; | |
222 | struct dsa_port *dp; | |
223 | int device, port; | |
224 | ||
225 | /* DSA currently only supports a single CPU port */ | |
226 | dst->cpu_dp = dsa_tree_find_first_cpu(dst); | |
227 | if (!dst->cpu_dp) { | |
228 | pr_warn("Tree has no master device\n"); | |
229 | return -EINVAL; | |
230 | } | |
231 | ||
232 | /* Assign the default CPU port to all ports of the fabric */ | |
233 | for (device = 0; device < DSA_MAX_SWITCHES; device++) { | |
234 | ds = dst->ds[device]; | |
235 | if (!ds) | |
236 | continue; | |
237 | ||
238 | for (port = 0; port < ds->num_ports; port++) { | |
239 | dp = &ds->ports[port]; | |
240 | ||
986d7ccf | 241 | if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) |
f070464c VD |
242 | dp->cpu_dp = dst->cpu_dp; |
243 | } | |
244 | } | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst) | |
250 | { | |
251 | /* DSA currently only supports a single CPU port */ | |
252 | dst->cpu_dp = NULL; | |
253 | } | |
254 | ||
1d27732f | 255 | static int dsa_port_setup(struct dsa_port *dp) |
83c0afae | 256 | { |
d8ba3620 | 257 | enum devlink_port_flavour flavour; |
1d27732f | 258 | struct dsa_switch *ds = dp->ds; |
15b04ace | 259 | struct dsa_switch_tree *dst = ds->dst; |
d8ba3620 JP |
260 | int err; |
261 | ||
262 | if (dp->type == DSA_PORT_TYPE_UNUSED) | |
263 | return 0; | |
83c0afae | 264 | |
1d27732f | 265 | memset(&dp->devlink_port, 0, sizeof(dp->devlink_port)); |
a2c7023f | 266 | dp->mac = of_get_mac_address(dp->dn); |
83c0afae | 267 | |
d8ba3620 JP |
268 | switch (dp->type) { |
269 | case DSA_PORT_TYPE_CPU: | |
270 | flavour = DEVLINK_PORT_FLAVOUR_CPU; | |
271 | break; | |
272 | case DSA_PORT_TYPE_DSA: | |
273 | flavour = DEVLINK_PORT_FLAVOUR_DSA; | |
274 | break; | |
275 | case DSA_PORT_TYPE_USER: /* fall-through */ | |
276 | default: | |
277 | flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; | |
278 | break; | |
279 | } | |
280 | ||
281 | /* dp->index is used now as port_number. However | |
282 | * CPU and DSA ports should have separate numbering | |
283 | * independent from front panel port numbers. | |
284 | */ | |
285 | devlink_port_attrs_set(&dp->devlink_port, flavour, | |
15b04ace JP |
286 | dp->index, false, 0, |
287 | (const char *) &dst->index, sizeof(dst->index)); | |
d8ba3620 JP |
288 | err = devlink_port_register(ds->devlink, &dp->devlink_port, |
289 | dp->index); | |
1d27732f | 290 | if (err) |
83c0afae | 291 | return err; |
83c0afae | 292 | |
1d27732f VD |
293 | switch (dp->type) { |
294 | case DSA_PORT_TYPE_UNUSED: | |
295 | break; | |
296 | case DSA_PORT_TYPE_CPU: | |
da077392 JP |
297 | err = dsa_port_link_register_of(dp); |
298 | if (err) { | |
299 | dev_err(ds->dev, "failed to setup link for port %d.%d\n", | |
300 | ds->index, dp->index); | |
301 | return err; | |
302 | } | |
303 | break; | |
1d27732f | 304 | case DSA_PORT_TYPE_DSA: |
33615367 | 305 | err = dsa_port_link_register_of(dp); |
1d27732f | 306 | if (err) { |
33615367 | 307 | dev_err(ds->dev, "failed to setup link for port %d.%d\n", |
1d27732f VD |
308 | ds->index, dp->index); |
309 | return err; | |
310 | } | |
1d27732f VD |
311 | break; |
312 | case DSA_PORT_TYPE_USER: | |
313 | err = dsa_slave_create(dp); | |
314 | if (err) | |
315 | dev_err(ds->dev, "failed to create slave for port %d.%d\n", | |
316 | ds->index, dp->index); | |
317 | else | |
318 | devlink_port_type_eth_set(&dp->devlink_port, dp->slave); | |
319 | break; | |
83c0afae AL |
320 | } |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
1d27732f | 325 | static void dsa_port_teardown(struct dsa_port *dp) |
83c0afae | 326 | { |
5447d786 FF |
327 | if (dp->type != DSA_PORT_TYPE_UNUSED) |
328 | devlink_port_unregister(&dp->devlink_port); | |
1d27732f VD |
329 | |
330 | switch (dp->type) { | |
331 | case DSA_PORT_TYPE_UNUSED: | |
332 | break; | |
333 | case DSA_PORT_TYPE_CPU: | |
4dad81ee AL |
334 | dsa_tag_driver_put(dp->tag_ops); |
335 | /* fall-through */ | |
1d27732f | 336 | case DSA_PORT_TYPE_DSA: |
33615367 | 337 | dsa_port_link_unregister_of(dp); |
1d27732f VD |
338 | break; |
339 | case DSA_PORT_TYPE_USER: | |
340 | if (dp->slave) { | |
341 | dsa_slave_destroy(dp->slave); | |
342 | dp->slave = NULL; | |
343 | } | |
344 | break; | |
83c0afae AL |
345 | } |
346 | } | |
347 | ||
1f08f9e9 | 348 | static int dsa_switch_setup(struct dsa_switch *ds) |
83c0afae | 349 | { |
83c0afae AL |
350 | int err; |
351 | ||
6e830d8f | 352 | /* Initialize ds->phys_mii_mask before registering the slave MDIO bus |
9d490b4e | 353 | * driver and before ops->setup() has run, since the switch drivers and |
6e830d8f FF |
354 | * the slave MDIO bus driver rely on these values for probing PHY |
355 | * devices or not | |
356 | */ | |
02bc6e54 | 357 | ds->phys_mii_mask |= dsa_user_ports(ds); |
6e830d8f | 358 | |
96567d5d AL |
359 | /* Add the switch to devlink before calling setup, so that setup can |
360 | * add dpipe tables | |
361 | */ | |
362 | ds->devlink = devlink_alloc(&dsa_devlink_ops, 0); | |
363 | if (!ds->devlink) | |
364 | return -ENOMEM; | |
365 | ||
366 | err = devlink_register(ds->devlink, ds->dev); | |
367 | if (err) | |
368 | return err; | |
369 | ||
f515f192 VD |
370 | err = dsa_switch_register_notifier(ds); |
371 | if (err) | |
372 | return err; | |
373 | ||
b2243b36 VO |
374 | err = ds->ops->setup(ds); |
375 | if (err < 0) | |
376 | return err; | |
377 | ||
9d490b4e | 378 | if (!ds->slave_mii_bus && ds->ops->phy_read) { |
1eb59443 FF |
379 | ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); |
380 | if (!ds->slave_mii_bus) | |
381 | return -ENOMEM; | |
382 | ||
383 | dsa_slave_mii_bus_init(ds); | |
384 | ||
385 | err = mdiobus_register(ds->slave_mii_bus); | |
386 | if (err < 0) | |
387 | return err; | |
388 | } | |
389 | ||
83c0afae AL |
390 | return 0; |
391 | } | |
392 | ||
1f08f9e9 | 393 | static void dsa_switch_teardown(struct dsa_switch *ds) |
83c0afae | 394 | { |
9d490b4e | 395 | if (ds->slave_mii_bus && ds->ops->phy_read) |
1eb59443 | 396 | mdiobus_unregister(ds->slave_mii_bus); |
f515f192 VD |
397 | |
398 | dsa_switch_unregister_notifier(ds); | |
96567d5d AL |
399 | |
400 | if (ds->devlink) { | |
401 | devlink_unregister(ds->devlink); | |
402 | devlink_free(ds->devlink); | |
403 | ds->devlink = NULL; | |
404 | } | |
405 | ||
83c0afae AL |
406 | } |
407 | ||
1f08f9e9 VD |
408 | static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) |
409 | { | |
410 | struct dsa_switch *ds; | |
1d27732f VD |
411 | struct dsa_port *dp; |
412 | int device, port; | |
1f08f9e9 VD |
413 | int err; |
414 | ||
415 | for (device = 0; device < DSA_MAX_SWITCHES; device++) { | |
416 | ds = dst->ds[device]; | |
417 | if (!ds) | |
418 | continue; | |
419 | ||
420 | err = dsa_switch_setup(ds); | |
421 | if (err) | |
422 | return err; | |
1d27732f VD |
423 | |
424 | for (port = 0; port < ds->num_ports; port++) { | |
425 | dp = &ds->ports[port]; | |
426 | ||
427 | err = dsa_port_setup(dp); | |
428 | if (err) | |
429 | return err; | |
430 | } | |
1f08f9e9 VD |
431 | } |
432 | ||
433 | return 0; | |
434 | } | |
435 | ||
436 | static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) | |
437 | { | |
438 | struct dsa_switch *ds; | |
1d27732f VD |
439 | struct dsa_port *dp; |
440 | int device, port; | |
1f08f9e9 VD |
441 | |
442 | for (device = 0; device < DSA_MAX_SWITCHES; device++) { | |
443 | ds = dst->ds[device]; | |
444 | if (!ds) | |
445 | continue; | |
446 | ||
1d27732f VD |
447 | for (port = 0; port < ds->num_ports; port++) { |
448 | dp = &ds->ports[port]; | |
449 | ||
450 | dsa_port_teardown(dp); | |
451 | } | |
452 | ||
1f08f9e9 VD |
453 | dsa_switch_teardown(ds); |
454 | } | |
455 | } | |
456 | ||
17a22fcf VD |
457 | static int dsa_tree_setup_master(struct dsa_switch_tree *dst) |
458 | { | |
459 | struct dsa_port *cpu_dp = dst->cpu_dp; | |
460 | struct net_device *master = cpu_dp->master; | |
461 | ||
462 | /* DSA currently supports a single pair of CPU port and master device */ | |
463 | return dsa_master_setup(master, cpu_dp); | |
464 | } | |
465 | ||
466 | static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) | |
467 | { | |
468 | struct dsa_port *cpu_dp = dst->cpu_dp; | |
469 | struct net_device *master = cpu_dp->master; | |
470 | ||
471 | return dsa_master_teardown(master); | |
472 | } | |
473 | ||
ec15dd42 | 474 | static int dsa_tree_setup(struct dsa_switch_tree *dst) |
83c0afae | 475 | { |
34c09a89 | 476 | bool complete; |
83c0afae AL |
477 | int err; |
478 | ||
ec15dd42 VD |
479 | if (dst->setup) { |
480 | pr_err("DSA: tree %d already setup! Disjoint trees?\n", | |
481 | dst->index); | |
482 | return -EEXIST; | |
483 | } | |
484 | ||
34c09a89 VD |
485 | complete = dsa_tree_setup_routing_table(dst); |
486 | if (!complete) | |
487 | return 0; | |
488 | ||
f070464c VD |
489 | err = dsa_tree_setup_default_cpu(dst); |
490 | if (err) | |
491 | return err; | |
492 | ||
1f08f9e9 VD |
493 | err = dsa_tree_setup_switches(dst); |
494 | if (err) | |
495 | return err; | |
83c0afae | 496 | |
17a22fcf | 497 | err = dsa_tree_setup_master(dst); |
1943563d VD |
498 | if (err) |
499 | return err; | |
500 | ||
ec15dd42 VD |
501 | dst->setup = true; |
502 | ||
503 | pr_info("DSA: tree %d setup\n", dst->index); | |
83c0afae AL |
504 | |
505 | return 0; | |
506 | } | |
507 | ||
ec15dd42 | 508 | static void dsa_tree_teardown(struct dsa_switch_tree *dst) |
83c0afae | 509 | { |
ec15dd42 | 510 | if (!dst->setup) |
83c0afae AL |
511 | return; |
512 | ||
17a22fcf | 513 | dsa_tree_teardown_master(dst); |
83c0afae | 514 | |
1f08f9e9 | 515 | dsa_tree_teardown_switches(dst); |
83c0afae | 516 | |
f070464c | 517 | dsa_tree_teardown_default_cpu(dst); |
0c73c523 | 518 | |
ec15dd42 VD |
519 | pr_info("DSA: tree %d torn down\n", dst->index); |
520 | ||
521 | dst->setup = false; | |
83c0afae AL |
522 | } |
523 | ||
6da2a940 VD |
524 | static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, |
525 | unsigned int index) | |
526 | { | |
30817354 VD |
527 | dsa_tree_teardown(dst); |
528 | ||
6da2a940 VD |
529 | dst->ds[index] = NULL; |
530 | dsa_tree_put(dst); | |
531 | } | |
532 | ||
533 | static int dsa_tree_add_switch(struct dsa_switch_tree *dst, | |
534 | struct dsa_switch *ds) | |
535 | { | |
536 | unsigned int index = ds->index; | |
30817354 | 537 | int err; |
6da2a940 VD |
538 | |
539 | if (dst->ds[index]) | |
540 | return -EBUSY; | |
541 | ||
542 | dsa_tree_get(dst); | |
543 | dst->ds[index] = ds; | |
544 | ||
30817354 VD |
545 | err = dsa_tree_setup(dst); |
546 | if (err) | |
547 | dsa_tree_remove_switch(dst, index); | |
548 | ||
549 | return err; | |
6da2a940 VD |
550 | } |
551 | ||
06e24d08 VD |
552 | static int dsa_port_parse_user(struct dsa_port *dp, const char *name) |
553 | { | |
554 | if (!name) | |
555 | name = "eth%d"; | |
556 | ||
557 | dp->type = DSA_PORT_TYPE_USER; | |
558 | dp->name = name; | |
559 | ||
560 | return 0; | |
561 | } | |
562 | ||
563 | static int dsa_port_parse_dsa(struct dsa_port *dp) | |
564 | { | |
565 | dp->type = DSA_PORT_TYPE_DSA; | |
566 | ||
567 | return 0; | |
568 | } | |
569 | ||
570 | static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) | |
571 | { | |
7354fcb0 VD |
572 | struct dsa_switch *ds = dp->ds; |
573 | struct dsa_switch_tree *dst = ds->dst; | |
574 | const struct dsa_device_ops *tag_ops; | |
575 | enum dsa_tag_protocol tag_protocol; | |
576 | ||
5ed4e3eb | 577 | tag_protocol = ds->ops->get_tag_protocol(ds, dp->index); |
c39e2a1d | 578 | tag_ops = dsa_tag_driver_get(tag_protocol); |
7354fcb0 VD |
579 | if (IS_ERR(tag_ops)) { |
580 | dev_warn(ds->dev, "No tagger for this switch\n"); | |
581 | return PTR_ERR(tag_ops); | |
582 | } | |
583 | ||
06e24d08 | 584 | dp->type = DSA_PORT_TYPE_CPU; |
cc1939e4 | 585 | dp->filter = tag_ops->filter; |
7354fcb0 VD |
586 | dp->rcv = tag_ops->rcv; |
587 | dp->tag_ops = tag_ops; | |
06e24d08 | 588 | dp->master = master; |
7354fcb0 | 589 | dp->dst = dst; |
06e24d08 VD |
590 | |
591 | return 0; | |
592 | } | |
593 | ||
fd223e2e VD |
594 | static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) |
595 | { | |
6d4e5c57 | 596 | struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); |
1838fa89 | 597 | const char *name = of_get_property(dn, "label", NULL); |
54df6fa9 | 598 | bool link = of_property_read_bool(dn, "link"); |
6d4e5c57 | 599 | |
06e24d08 VD |
600 | dp->dn = dn; |
601 | ||
6d4e5c57 | 602 | if (ethernet) { |
cbabb0ac VD |
603 | struct net_device *master; |
604 | ||
605 | master = of_find_net_device_by_node(ethernet); | |
606 | if (!master) | |
607 | return -EPROBE_DEFER; | |
608 | ||
06e24d08 | 609 | return dsa_port_parse_cpu(dp, master); |
6d4e5c57 VD |
610 | } |
611 | ||
06e24d08 VD |
612 | if (link) |
613 | return dsa_port_parse_dsa(dp); | |
fd223e2e | 614 | |
06e24d08 | 615 | return dsa_port_parse_user(dp, name); |
fd223e2e VD |
616 | } |
617 | ||
975e6e32 VD |
618 | static int dsa_switch_parse_ports_of(struct dsa_switch *ds, |
619 | struct device_node *dn) | |
83c0afae | 620 | { |
5b32fe07 | 621 | struct device_node *ports, *port; |
fd223e2e | 622 | struct dsa_port *dp; |
9919a363 | 623 | int err = 0; |
83c0afae | 624 | u32 reg; |
5b32fe07 VD |
625 | |
626 | ports = of_get_child_by_name(dn, "ports"); | |
627 | if (!ports) { | |
628 | dev_err(ds->dev, "no ports child node found\n"); | |
629 | return -EINVAL; | |
630 | } | |
83c0afae AL |
631 | |
632 | for_each_available_child_of_node(ports, port) { | |
633 | err = of_property_read_u32(port, "reg", ®); | |
634 | if (err) | |
9919a363 | 635 | goto out_put_node; |
83c0afae | 636 | |
9919a363 WY |
637 | if (reg >= ds->num_ports) { |
638 | err = -EINVAL; | |
639 | goto out_put_node; | |
640 | } | |
83c0afae | 641 | |
fd223e2e VD |
642 | dp = &ds->ports[reg]; |
643 | ||
644 | err = dsa_port_parse_of(dp, port); | |
645 | if (err) | |
9919a363 | 646 | goto out_put_node; |
83c0afae AL |
647 | } |
648 | ||
9919a363 WY |
649 | out_put_node: |
650 | of_node_put(ports); | |
651 | return err; | |
83c0afae AL |
652 | } |
653 | ||
975e6e32 VD |
654 | static int dsa_switch_parse_member_of(struct dsa_switch *ds, |
655 | struct device_node *dn) | |
656 | { | |
657 | u32 m[2] = { 0, 0 }; | |
658 | int sz; | |
659 | ||
660 | /* Don't error out if this optional property isn't found */ | |
661 | sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2); | |
662 | if (sz < 0 && sz != -EINVAL) | |
663 | return sz; | |
664 | ||
665 | ds->index = m[1]; | |
666 | if (ds->index >= DSA_MAX_SWITCHES) | |
667 | return -EINVAL; | |
668 | ||
669 | ds->dst = dsa_tree_touch(m[0]); | |
670 | if (!ds->dst) | |
671 | return -ENOMEM; | |
672 | ||
673 | return 0; | |
674 | } | |
675 | ||
676 | static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) | |
677 | { | |
678 | int err; | |
679 | ||
680 | err = dsa_switch_parse_member_of(ds, dn); | |
681 | if (err) | |
682 | return err; | |
683 | ||
684 | return dsa_switch_parse_ports_of(ds, dn); | |
685 | } | |
686 | ||
fd223e2e VD |
687 | static int dsa_port_parse(struct dsa_port *dp, const char *name, |
688 | struct device *dev) | |
689 | { | |
6d4e5c57 | 690 | if (!strcmp(name, "cpu")) { |
cbabb0ac VD |
691 | struct net_device *master; |
692 | ||
693 | master = dsa_dev_to_net_device(dev); | |
694 | if (!master) | |
695 | return -EPROBE_DEFER; | |
696 | ||
697 | dev_put(master); | |
698 | ||
06e24d08 | 699 | return dsa_port_parse_cpu(dp, master); |
6d4e5c57 VD |
700 | } |
701 | ||
06e24d08 VD |
702 | if (!strcmp(name, "dsa")) |
703 | return dsa_port_parse_dsa(dp); | |
fd223e2e | 704 | |
06e24d08 | 705 | return dsa_port_parse_user(dp, name); |
fd223e2e VD |
706 | } |
707 | ||
975e6e32 VD |
708 | static int dsa_switch_parse_ports(struct dsa_switch *ds, |
709 | struct dsa_chip_data *cd) | |
71e0bbde FF |
710 | { |
711 | bool valid_name_found = false; | |
fd223e2e VD |
712 | struct dsa_port *dp; |
713 | struct device *dev; | |
714 | const char *name; | |
71e0bbde | 715 | unsigned int i; |
fd223e2e | 716 | int err; |
71e0bbde FF |
717 | |
718 | for (i = 0; i < DSA_MAX_PORTS; i++) { | |
fd223e2e VD |
719 | name = cd->port_names[i]; |
720 | dev = cd->netdev[i]; | |
721 | dp = &ds->ports[i]; | |
722 | ||
723 | if (!name) | |
71e0bbde FF |
724 | continue; |
725 | ||
fd223e2e VD |
726 | err = dsa_port_parse(dp, name, dev); |
727 | if (err) | |
728 | return err; | |
729 | ||
71e0bbde FF |
730 | valid_name_found = true; |
731 | } | |
732 | ||
733 | if (!valid_name_found && i == DSA_MAX_PORTS) | |
734 | return -EINVAL; | |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
975e6e32 | 739 | static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) |
71e0bbde | 740 | { |
975e6e32 | 741 | ds->cd = cd; |
71e0bbde | 742 | |
975e6e32 VD |
743 | /* We don't support interconnected switches nor multiple trees via |
744 | * platform data, so this is the unique switch of the tree. | |
745 | */ | |
746 | ds->index = 0; | |
747 | ds->dst = dsa_tree_touch(0); | |
748 | if (!ds->dst) | |
749 | return -ENOMEM; | |
71e0bbde | 750 | |
975e6e32 | 751 | return dsa_switch_parse_ports(ds, cd); |
71e0bbde FF |
752 | } |
753 | ||
30817354 VD |
754 | static int dsa_switch_add(struct dsa_switch *ds) |
755 | { | |
756 | struct dsa_switch_tree *dst = ds->dst; | |
757 | ||
758 | return dsa_tree_add_switch(dst, ds); | |
759 | } | |
760 | ||
b4fbb347 | 761 | static int dsa_switch_probe(struct dsa_switch *ds) |
83c0afae | 762 | { |
23c9ee49 VD |
763 | struct dsa_chip_data *pdata = ds->dev->platform_data; |
764 | struct device_node *np = ds->dev->of_node; | |
34c09a89 | 765 | int err; |
83c0afae | 766 | |
975e6e32 VD |
767 | if (np) |
768 | err = dsa_switch_parse_of(ds, np); | |
769 | else if (pdata) | |
770 | err = dsa_switch_parse(ds, pdata); | |
771 | else | |
772 | err = -ENODEV; | |
83c0afae | 773 | |
975e6e32 VD |
774 | if (err) |
775 | return err; | |
d390238c | 776 | |
30817354 | 777 | return dsa_switch_add(ds); |
83c0afae AL |
778 | } |
779 | ||
a0c02161 VD |
780 | struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n) |
781 | { | |
a0c02161 | 782 | struct dsa_switch *ds; |
818be848 | 783 | int i; |
a0c02161 | 784 | |
33b363e0 | 785 | ds = devm_kzalloc(dev, struct_size(ds, ports, n), GFP_KERNEL); |
a0c02161 VD |
786 | if (!ds) |
787 | return NULL; | |
788 | ||
0015b80a SM |
789 | /* We avoid allocating memory outside dsa_switch |
790 | * if it is not needed. | |
791 | */ | |
792 | if (n <= sizeof(ds->_bitmap) * 8) { | |
793 | ds->bitmap = &ds->_bitmap; | |
794 | } else { | |
795 | ds->bitmap = devm_kcalloc(dev, | |
796 | BITS_TO_LONGS(n), | |
797 | sizeof(unsigned long), | |
798 | GFP_KERNEL); | |
799 | if (unlikely(!ds->bitmap)) | |
800 | return NULL; | |
801 | } | |
802 | ||
a0c02161 VD |
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); | |
b4fbb347 | 820 | err = dsa_switch_probe(ds); |
9e741045 | 821 | dsa_tree_put(ds->dst); |
83c0afae AL |
822 | mutex_unlock(&dsa2_mutex); |
823 | ||
824 | return err; | |
825 | } | |
826 | EXPORT_SYMBOL_GPL(dsa_register_switch); | |
827 | ||
b4fbb347 | 828 | static void dsa_switch_remove(struct dsa_switch *ds) |
83c0afae AL |
829 | { |
830 | struct dsa_switch_tree *dst = ds->dst; | |
6da2a940 | 831 | unsigned int index = ds->index; |
83c0afae | 832 | |
6da2a940 | 833 | dsa_tree_remove_switch(dst, index); |
83c0afae AL |
834 | } |
835 | ||
836 | void dsa_unregister_switch(struct dsa_switch *ds) | |
837 | { | |
838 | mutex_lock(&dsa2_mutex); | |
b4fbb347 | 839 | dsa_switch_remove(ds); |
83c0afae AL |
840 | mutex_unlock(&dsa2_mutex); |
841 | } | |
842 | EXPORT_SYMBOL_GPL(dsa_unregister_switch); |