Skip to content

Commit 98cd155

Browse files
ffainellidavem330
authored andcommitted
net: dsa: Mock-up driver
This patch adds support for a DSA mock-up driver which essentially does the following: - registers/unregisters 4 fixed PHYs to the slave network devices - uses eth0 (configurable) as the master netdev - registers the switch as a fixed MDIO device against the fixed MDIO bus at address 31 - includes dynamic debug prints for dsa_switch_ops functions that can be enabled to get call traces This is a good way to test modular builds as well as exercise the DSA APIs without requiring access to real hardware. This does not test the data-path, although this could be added later on. Signed-off-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 772c3bd commit 98cd155

File tree

5 files changed

+390
-1
lines changed

5 files changed

+390
-1
lines changed

drivers/net/dsa/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,12 @@ config NET_DSA_QCA8K
3434
This enables support for the Qualcomm Atheros QCA8K Ethernet
3535
switch chips.
3636

37+
config NET_DSA_LOOP
38+
tristate "DSA mock-up Ethernet switch chip support"
39+
depends on NET_DSA
40+
select FIXED_PHY
41+
---help---
42+
This enables support for a fake mock-up switch chip which
43+
exercises the DSA APIs.
44+
3745
endmenu

drivers/net/dsa/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
22
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o
33
bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o
44
obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
5-
65
obj-y += b53/
76
obj-y += mv88e6xxx/
7+
obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o

drivers/net/dsa/dsa_loop.c

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
/*
2+
* Distributed Switch Architecture loopback driver
3+
*
4+
* Copyright (C) 2016, Florian Fainelli <[email protected]>
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*/
11+
12+
#include <linux/platform_device.h>
13+
#include <linux/netdevice.h>
14+
#include <linux/phy.h>
15+
#include <linux/phy_fixed.h>
16+
#include <linux/export.h>
17+
#include <linux/workqueue.h>
18+
#include <linux/module.h>
19+
#include <linux/if_bridge.h>
20+
#include <net/switchdev.h>
21+
#include <net/dsa.h>
22+
23+
#include "dsa_loop.h"
24+
25+
struct dsa_loop_vlan {
26+
u16 members;
27+
u16 untagged;
28+
};
29+
30+
#define DSA_LOOP_VLANS 5
31+
32+
struct dsa_loop_priv {
33+
struct mii_bus *bus;
34+
unsigned int port_base;
35+
struct dsa_loop_vlan vlans[DSA_LOOP_VLANS];
36+
struct net_device *netdev;
37+
u16 pvid;
38+
};
39+
40+
static struct phy_device *phydevs[PHY_MAX_ADDR];
41+
42+
static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds)
43+
{
44+
dev_dbg(ds->dev, "%s\n", __func__);
45+
46+
return DSA_TAG_PROTO_NONE;
47+
}
48+
49+
static int dsa_loop_setup(struct dsa_switch *ds)
50+
{
51+
dev_dbg(ds->dev, "%s\n", __func__);
52+
53+
return 0;
54+
}
55+
56+
static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr)
57+
{
58+
dev_dbg(ds->dev, "%s\n", __func__);
59+
60+
return 0;
61+
}
62+
63+
static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum)
64+
{
65+
struct dsa_loop_priv *ps = ds->priv;
66+
struct mii_bus *bus = ps->bus;
67+
68+
dev_dbg(ds->dev, "%s\n", __func__);
69+
70+
return mdiobus_read_nested(bus, ps->port_base + port, regnum);
71+
}
72+
73+
static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
74+
int regnum, u16 value)
75+
{
76+
struct dsa_loop_priv *ps = ds->priv;
77+
struct mii_bus *bus = ps->bus;
78+
79+
dev_dbg(ds->dev, "%s\n", __func__);
80+
81+
return mdiobus_write_nested(bus, ps->port_base + port, regnum, value);
82+
}
83+
84+
static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
85+
struct net_device *bridge)
86+
{
87+
dev_dbg(ds->dev, "%s\n", __func__);
88+
89+
return 0;
90+
}
91+
92+
static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port,
93+
struct net_device *bridge)
94+
{
95+
dev_dbg(ds->dev, "%s\n", __func__);
96+
}
97+
98+
static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
99+
u8 state)
100+
{
101+
dev_dbg(ds->dev, "%s\n", __func__);
102+
}
103+
104+
static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
105+
bool vlan_filtering)
106+
{
107+
dev_dbg(ds->dev, "%s\n", __func__);
108+
109+
return 0;
110+
}
111+
112+
static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port,
113+
const struct switchdev_obj_port_vlan *vlan,
114+
struct switchdev_trans *trans)
115+
{
116+
struct dsa_loop_priv *ps = ds->priv;
117+
struct mii_bus *bus = ps->bus;
118+
119+
dev_dbg(ds->dev, "%s\n", __func__);
120+
121+
/* Just do a sleeping operation to make lockdep checks effective */
122+
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
123+
124+
if (vlan->vid_end > DSA_LOOP_VLANS)
125+
return -ERANGE;
126+
127+
return 0;
128+
}
129+
130+
static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,
131+
const struct switchdev_obj_port_vlan *vlan,
132+
struct switchdev_trans *trans)
133+
{
134+
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
135+
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
136+
struct dsa_loop_priv *ps = ds->priv;
137+
struct mii_bus *bus = ps->bus;
138+
struct dsa_loop_vlan *vl;
139+
u16 vid;
140+
141+
dev_dbg(ds->dev, "%s\n", __func__);
142+
143+
/* Just do a sleeping operation to make lockdep checks effective */
144+
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
145+
146+
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
147+
vl = &ps->vlans[vid];
148+
149+
vl->members |= BIT(port);
150+
if (untagged)
151+
vl->untagged |= BIT(port);
152+
else
153+
vl->untagged &= ~BIT(port);
154+
}
155+
156+
if (pvid)
157+
ps->pvid = vid;
158+
}
159+
160+
static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
161+
const struct switchdev_obj_port_vlan *vlan)
162+
{
163+
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
164+
struct dsa_loop_priv *ps = ds->priv;
165+
struct mii_bus *bus = ps->bus;
166+
struct dsa_loop_vlan *vl;
167+
u16 vid, pvid;
168+
169+
dev_dbg(ds->dev, "%s\n", __func__);
170+
171+
/* Just do a sleeping operation to make lockdep checks effective */
172+
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
173+
174+
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
175+
vl = &ps->vlans[vid];
176+
177+
vl->members &= ~BIT(port);
178+
if (untagged)
179+
vl->untagged &= ~BIT(port);
180+
181+
if (pvid == vid)
182+
pvid = 1;
183+
}
184+
ps->pvid = pvid;
185+
186+
return 0;
187+
}
188+
189+
static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port,
190+
struct switchdev_obj_port_vlan *vlan,
191+
int (*cb)(struct switchdev_obj *obj))
192+
{
193+
struct dsa_loop_priv *ps = ds->priv;
194+
struct mii_bus *bus = ps->bus;
195+
struct dsa_loop_vlan *vl;
196+
u16 vid, vid_start = 0;
197+
int err;
198+
199+
dev_dbg(ds->dev, "%s\n", __func__);
200+
201+
/* Just do a sleeping operation to make lockdep checks effective */
202+
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
203+
204+
for (vid = vid_start; vid < DSA_LOOP_VLANS; vid++) {
205+
vl = &ps->vlans[vid];
206+
207+
if (!(vl->members & BIT(port)))
208+
continue;
209+
210+
vlan->vid_begin = vlan->vid_end = vid;
211+
vlan->flags = 0;
212+
213+
if (vl->untagged & BIT(port))
214+
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
215+
if (ps->pvid == vid)
216+
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
217+
218+
err = cb(&vlan->obj);
219+
if (err)
220+
break;
221+
}
222+
223+
return err;
224+
}
225+
226+
static struct dsa_switch_ops dsa_loop_driver = {
227+
.get_tag_protocol = dsa_loop_get_protocol,
228+
.setup = dsa_loop_setup,
229+
.set_addr = dsa_loop_set_addr,
230+
.phy_read = dsa_loop_phy_read,
231+
.phy_write = dsa_loop_phy_write,
232+
.port_bridge_join = dsa_loop_port_bridge_join,
233+
.port_bridge_leave = dsa_loop_port_bridge_leave,
234+
.port_stp_state_set = dsa_loop_port_stp_state_set,
235+
.port_vlan_filtering = dsa_loop_port_vlan_filtering,
236+
.port_vlan_prepare = dsa_loop_port_vlan_prepare,
237+
.port_vlan_add = dsa_loop_port_vlan_add,
238+
.port_vlan_del = dsa_loop_port_vlan_del,
239+
.port_vlan_dump = dsa_loop_port_vlan_dump,
240+
};
241+
242+
static int dsa_loop_drv_probe(struct mdio_device *mdiodev)
243+
{
244+
struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data;
245+
struct dsa_loop_priv *ps;
246+
struct dsa_switch *ds;
247+
248+
if (!pdata)
249+
return -ENODEV;
250+
251+
dev_info(&mdiodev->dev, "%s: 0x%0x\n",
252+
pdata->name, pdata->enabled_ports);
253+
254+
ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
255+
if (!ds)
256+
return -ENOMEM;
257+
258+
ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL);
259+
ps->netdev = dev_get_by_name(&init_net, pdata->netdev);
260+
if (!ps->netdev)
261+
return -EPROBE_DEFER;
262+
263+
pdata->cd.netdev[DSA_LOOP_CPU_PORT] = &ps->netdev->dev;
264+
265+
ds->dev = &mdiodev->dev;
266+
ds->ops = &dsa_loop_driver;
267+
ds->priv = ps;
268+
ps->bus = mdiodev->bus;
269+
270+
dev_set_drvdata(&mdiodev->dev, ds);
271+
272+
return dsa_register_switch(ds, ds->dev);
273+
}
274+
275+
static void dsa_loop_drv_remove(struct mdio_device *mdiodev)
276+
{
277+
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
278+
struct dsa_loop_priv *ps = ds->priv;
279+
280+
dsa_unregister_switch(ds);
281+
dev_put(ps->netdev);
282+
}
283+
284+
static struct mdio_driver dsa_loop_drv = {
285+
.mdiodrv.driver = {
286+
.name = "dsa-loop",
287+
},
288+
.probe = dsa_loop_drv_probe,
289+
.remove = dsa_loop_drv_remove,
290+
};
291+
292+
#define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2)
293+
294+
static void unregister_fixed_phys(void)
295+
{
296+
unsigned int i;
297+
298+
for (i = 0; i < NUM_FIXED_PHYS; i++)
299+
if (phydevs[i])
300+
fixed_phy_unregister(phydevs[i]);
301+
}
302+
303+
static int __init dsa_loop_init(void)
304+
{
305+
struct fixed_phy_status status = {
306+
.link = 1,
307+
.speed = SPEED_100,
308+
.duplex = DUPLEX_FULL,
309+
};
310+
unsigned int i;
311+
312+
for (i = 0; i < NUM_FIXED_PHYS; i++)
313+
phydevs[i] = fixed_phy_register(PHY_POLL, &status, -1, NULL);
314+
315+
return mdio_driver_register(&dsa_loop_drv);
316+
}
317+
module_init(dsa_loop_init);
318+
319+
static void __exit dsa_loop_exit(void)
320+
{
321+
mdio_driver_unregister(&dsa_loop_drv);
322+
unregister_fixed_phys();
323+
}
324+
module_exit(dsa_loop_exit);
325+
326+
MODULE_LICENSE("GPL");
327+
MODULE_AUTHOR("Florian Fainelli");
328+
MODULE_DESCRIPTION("DSA loopback driver");

drivers/net/dsa/dsa_loop.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef __DSA_LOOP_H
2+
#define __DSA_LOOP_H
3+
4+
struct dsa_chip_data;
5+
6+
struct dsa_loop_pdata {
7+
/* Must be first, such that dsa_register_switch() can access this
8+
* without gory pointer manipulations
9+
*/
10+
struct dsa_chip_data cd;
11+
const char *name;
12+
unsigned int enabled_ports;
13+
const char *netdev;
14+
};
15+
16+
#define DSA_LOOP_NUM_PORTS 6
17+
#define DSA_LOOP_CPU_PORT (DSA_LOOP_NUM_PORTS - 1)
18+
19+
#endif /* __DSA_LOOP_H */

0 commit comments

Comments
 (0)