Skip to content

Commit 5e95329

Browse files
ffainellidavem330
authored andcommitted
dsa: add device tree bindings to register DSA switches
This patch adds support for registering DSA switches using Device Tree bindings. Note that we support programming the switch routing table even though no in-tree user seems to require it. I tested this on Armada 370 with a Marvell 88E6172 (not supported by mainline yet). Signed-off-by: Florian Fainelli <florian@openwrt.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent eec2e61 commit 5e95329

File tree

2 files changed

+319
-5
lines changed
  • Documentation/devicetree/bindings/net/dsa
  • net/dsa

2 files changed

+319
-5
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
Marvell Distributed Switch Architecture Device Tree Bindings
2+
------------------------------------------------------------
3+
4+
Required properties:
5+
- compatible : Should be "marvell,dsa"
6+
- #address-cells : Must be 2, first cell is the address on the MDIO bus
7+
and second cell is the address in the switch tree.
8+
Second cell is used only when cascading/chaining.
9+
- #size-cells : Must be 0
10+
- dsa,ethernet : Should be a phandle to a valid Ethernet device node
11+
- dsa,mii-bus : Should be a phandle to a valid MDIO bus device node
12+
13+
Optionnal properties:
14+
- interrupts : property with a value describing the switch
15+
interrupt number (not supported by the driver)
16+
17+
A DSA node can contain multiple switch chips which are therefore child nodes of
18+
the parent DSA node. The maximum number of allowed child nodes is 4
19+
(DSA_MAX_SWITCHES).
20+
Each of these switch child nodes should have the following required properties:
21+
22+
- reg : Describes the switch address on the MII bus
23+
- #address-cells : Must be 1
24+
- #size-cells : Must be 0
25+
26+
A switch may have multiple "port" children nodes
27+
28+
Each port children node must have the following mandatory properties:
29+
- reg : Describes the port address in the switch
30+
- label : Describes the label associated with this port, special
31+
labels are "cpu" to indicate a CPU port and "dsa" to
32+
indicate an uplink/downlink port.
33+
34+
Note that a port labelled "dsa" will imply checking for the uplink phandle
35+
described below.
36+
37+
Optionnal property:
38+
- link : Should be a phandle to another switch's DSA port.
39+
This property is only used when switches are being
40+
chained/cascaded together.
41+
42+
Example:
43+
44+
dsa@0 {
45+
compatible = "marvell,dsa";
46+
#address-cells = <1>;
47+
#size-cells = <0>;
48+
49+
interrupts = <10>;
50+
dsa,ethernet = <&ethernet0>;
51+
dsa,mii-bus = <&mii_bus0>;
52+
53+
switch@0 {
54+
#address-cells = <1>;
55+
#size-cells = <0>;
56+
reg = <16 0>; /* MDIO address 16, switch 0 in tree */
57+
58+
port@0 {
59+
reg = <0>;
60+
label = "lan1";
61+
};
62+
63+
port@1 {
64+
reg = <1>;
65+
label = "lan2";
66+
};
67+
68+
port@5 {
69+
reg = <5>;
70+
label = "cpu";
71+
};
72+
73+
switch0uplink: port@6 {
74+
reg = <6>;
75+
label = "dsa";
76+
link = <&switch1uplink>;
77+
};
78+
};
79+
80+
switch@1 {
81+
#address-cells = <1>;
82+
#size-cells = <0>;
83+
reg = <17 1>; /* MDIO address 17, switch 1 in tree */
84+
85+
switch1uplink: port@0 {
86+
reg = <0>;
87+
label = "dsa";
88+
link = <&switch0uplink>;
89+
};
90+
};
91+
};

net/dsa/dsa.c

Lines changed: 228 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* net/dsa/dsa.c - Hardware switch handling
33
* Copyright (c) 2008-2009 Marvell Semiconductor
4+
* Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
45
*
56
* This program is free software; you can redistribute it and/or modify
67
* it under the terms of the GNU General Public License as published by
@@ -14,6 +15,9 @@
1415
#include <linux/slab.h>
1516
#include <linux/module.h>
1617
#include <net/dsa.h>
18+
#include <linux/of.h>
19+
#include <linux/of_mdio.h>
20+
#include <linux/of_platform.h>
1721
#include "dsa_priv.h"
1822

1923
char dsa_driver_version[] = "0.1";
@@ -287,34 +291,239 @@ static struct net_device *dev_to_net_device(struct device *dev)
287291
return NULL;
288292
}
289293

294+
#ifdef CONFIG_OF
295+
static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
296+
struct dsa_chip_data *cd,
297+
int chip_index,
298+
struct device_node *link)
299+
{
300+
int ret;
301+
const __be32 *reg;
302+
int link_port_addr;
303+
int link_sw_addr;
304+
struct device_node *parent_sw;
305+
int len;
306+
307+
parent_sw = of_get_parent(link);
308+
if (!parent_sw)
309+
return -EINVAL;
310+
311+
reg = of_get_property(parent_sw, "reg", &len);
312+
if (!reg || (len != sizeof(*reg) * 2))
313+
return -EINVAL;
314+
315+
link_sw_addr = be32_to_cpup(reg + 1);
316+
317+
if (link_sw_addr >= pd->nr_chips)
318+
return -EINVAL;
319+
320+
/* First time routing table allocation */
321+
if (!cd->rtable) {
322+
cd->rtable = kmalloc(pd->nr_chips * sizeof(s8), GFP_KERNEL);
323+
if (!cd->rtable)
324+
return -ENOMEM;
325+
326+
/* default to no valid uplink/downlink */
327+
memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
328+
}
329+
330+
reg = of_get_property(link, "reg", NULL);
331+
if (!reg) {
332+
ret = -EINVAL;
333+
goto out;
334+
}
335+
336+
link_port_addr = be32_to_cpup(reg);
337+
338+
cd->rtable[link_sw_addr] = link_port_addr;
339+
340+
return 0;
341+
out:
342+
kfree(cd->rtable);
343+
return ret;
344+
}
345+
346+
static int dsa_of_probe(struct platform_device *pdev)
347+
{
348+
struct device_node *np = pdev->dev.of_node;
349+
struct device_node *child, *mdio, *ethernet, *port, *link;
350+
struct mii_bus *mdio_bus;
351+
struct platform_device *ethernet_dev;
352+
struct dsa_platform_data *pd;
353+
struct dsa_chip_data *cd;
354+
const char *port_name;
355+
int chip_index, port_index;
356+
const unsigned int *sw_addr, *port_reg;
357+
int ret, i;
358+
359+
mdio = of_parse_phandle(np, "dsa,mii-bus", 0);
360+
if (!mdio)
361+
return -EINVAL;
362+
363+
mdio_bus = of_mdio_find_bus(mdio);
364+
if (!mdio_bus)
365+
return -EINVAL;
366+
367+
ethernet = of_parse_phandle(np, "dsa,ethernet", 0);
368+
if (!ethernet)
369+
return -EINVAL;
370+
371+
ethernet_dev = of_find_device_by_node(ethernet);
372+
if (!ethernet_dev)
373+
return -ENODEV;
374+
375+
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
376+
if (!pd)
377+
return -ENOMEM;
378+
379+
pdev->dev.platform_data = pd;
380+
pd->netdev = &ethernet_dev->dev;
381+
pd->nr_chips = of_get_child_count(np);
382+
if (pd->nr_chips > DSA_MAX_SWITCHES)
383+
pd->nr_chips = DSA_MAX_SWITCHES;
384+
385+
pd->chip = kzalloc(pd->nr_chips * sizeof(struct dsa_chip_data),
386+
GFP_KERNEL);
387+
if (!pd->chip) {
388+
ret = -ENOMEM;
389+
goto out_free;
390+
}
391+
392+
chip_index = 0;
393+
for_each_available_child_of_node(np, child) {
394+
cd = &pd->chip[chip_index];
395+
396+
cd->mii_bus = &mdio_bus->dev;
397+
398+
sw_addr = of_get_property(child, "reg", NULL);
399+
if (!sw_addr)
400+
continue;
401+
402+
cd->sw_addr = be32_to_cpup(sw_addr);
403+
if (cd->sw_addr > PHY_MAX_ADDR)
404+
continue;
405+
406+
for_each_available_child_of_node(child, port) {
407+
port_reg = of_get_property(port, "reg", NULL);
408+
if (!port_reg)
409+
continue;
410+
411+
port_index = be32_to_cpup(port_reg);
412+
413+
port_name = of_get_property(port, "label", NULL);
414+
if (!port_name)
415+
continue;
416+
417+
cd->port_names[port_index] = kstrdup(port_name,
418+
GFP_KERNEL);
419+
if (!cd->port_names[port_index]) {
420+
ret = -ENOMEM;
421+
goto out_free_chip;
422+
}
423+
424+
link = of_parse_phandle(port, "link", 0);
425+
426+
if (!strcmp(port_name, "dsa") && link &&
427+
pd->nr_chips > 1) {
428+
ret = dsa_of_setup_routing_table(pd, cd,
429+
chip_index, link);
430+
if (ret)
431+
goto out_free_chip;
432+
}
433+
434+
if (port_index == DSA_MAX_PORTS)
435+
break;
436+
}
437+
}
438+
439+
return 0;
440+
441+
out_free_chip:
442+
for (i = 0; i < pd->nr_chips; i++) {
443+
port_index = 0;
444+
while (pd->chip[i].port_names &&
445+
pd->chip[i].port_names[++port_index])
446+
kfree(pd->chip[i].port_names[port_index]);
447+
kfree(pd->chip[i].rtable);
448+
}
449+
kfree(pd->chip);
450+
out_free:
451+
kfree(pd);
452+
pdev->dev.platform_data = NULL;
453+
return ret;
454+
}
455+
456+
static void dsa_of_remove(struct platform_device *pdev)
457+
{
458+
struct dsa_platform_data *pd = pdev->dev.platform_data;
459+
int i;
460+
int port_index;
461+
462+
if (!pdev->dev.of_node)
463+
return;
464+
465+
for (i = 0; i < pd->nr_chips; i++) {
466+
port_index = 0;
467+
while (pd->chip[i].port_names &&
468+
pd->chip[i].port_names[++port_index])
469+
kfree(pd->chip[i].port_names[port_index]);
470+
kfree(pd->chip[i].rtable);
471+
}
472+
473+
kfree(pd->chip);
474+
kfree(pd);
475+
}
476+
#else
477+
static inline int dsa_of_probe(struct platform_device *pdev)
478+
{
479+
return 0;
480+
}
481+
482+
static inline void dsa_of_remove(struct platform_device *pdev)
483+
{
484+
}
485+
#endif
486+
290487
static int dsa_probe(struct platform_device *pdev)
291488
{
292489
static int dsa_version_printed;
293490
struct dsa_platform_data *pd = pdev->dev.platform_data;
294491
struct net_device *dev;
295492
struct dsa_switch_tree *dst;
296-
int i;
493+
int i, ret;
297494

298495
if (!dsa_version_printed++)
299496
printk(KERN_NOTICE "Distributed Switch Architecture "
300497
"driver version %s\n", dsa_driver_version);
301498

499+
if (pdev->dev.of_node) {
500+
ret = dsa_of_probe(pdev);
501+
if (ret)
502+
return ret;
503+
504+
pd = pdev->dev.platform_data;
505+
}
506+
302507
if (pd == NULL || pd->netdev == NULL)
303508
return -EINVAL;
304509

305510
dev = dev_to_net_device(pd->netdev);
306-
if (dev == NULL)
307-
return -EINVAL;
511+
if (dev == NULL) {
512+
ret = -EINVAL;
513+
goto out;
514+
}
308515

309516
if (dev->dsa_ptr != NULL) {
310517
dev_put(dev);
311-
return -EEXIST;
518+
ret = -EEXIST;
519+
goto out;
312520
}
313521

314522
dst = kzalloc(sizeof(*dst), GFP_KERNEL);
315523
if (dst == NULL) {
316524
dev_put(dev);
317-
return -ENOMEM;
525+
ret = -ENOMEM;
526+
goto out;
318527
}
319528

320529
platform_set_drvdata(pdev, dst);
@@ -366,6 +575,11 @@ static int dsa_probe(struct platform_device *pdev)
366575
}
367576

368577
return 0;
578+
579+
out:
580+
dsa_of_remove(pdev);
581+
582+
return ret;
369583
}
370584

371585
static int dsa_remove(struct platform_device *pdev)
@@ -385,20 +599,29 @@ static int dsa_remove(struct platform_device *pdev)
385599
dsa_switch_destroy(ds);
386600
}
387601

602+
dsa_of_remove(pdev);
603+
388604
return 0;
389605
}
390606

391607
static void dsa_shutdown(struct platform_device *pdev)
392608
{
393609
}
394610

611+
static const struct of_device_id dsa_of_match_table[] = {
612+
{ .compatible = "marvell,dsa", },
613+
{}
614+
};
615+
MODULE_DEVICE_TABLE(of, dsa_of_match_table);
616+
395617
static struct platform_driver dsa_driver = {
396618
.probe = dsa_probe,
397619
.remove = dsa_remove,
398620
.shutdown = dsa_shutdown,
399621
.driver = {
400622
.name = "dsa",
401623
.owner = THIS_MODULE,
624+
.of_match_table = dsa_of_match_table,
402625
},
403626
};
404627

0 commit comments

Comments
 (0)