aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2017-03-30 21:43:21 -0400
committerDavid S. Miller <davem@davemloft.net>2017-04-01 15:39:32 -0400
commit98cd1552ea27e512c7e99e2aa76042a26e4fb25c (patch)
treeaba74b9bf561693e0fae899c347b33cd8698eec5
parent772c3bdad10db607b1035ae74cc817e81a114021 (diff)
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 <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/dsa/Kconfig8
-rw-r--r--drivers/net/dsa/Makefile2
-rw-r--r--drivers/net/dsa/dsa_loop.c328
-rw-r--r--drivers/net/dsa/dsa_loop.h19
-rw-r--r--drivers/net/dsa/dsa_loop_bdinfo.c34
5 files changed, 390 insertions, 1 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 564b267c8428..ba2e655eec19 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -34,4 +34,12 @@ config NET_DSA_QCA8K
34 This enables support for the Qualcomm Atheros QCA8K Ethernet 34 This enables support for the Qualcomm Atheros QCA8K Ethernet
35 switch chips. 35 switch chips.
36 36
37config 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
37endmenu 45endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index a3c941632217..5c8830991041 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -2,6 +2,6 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
2obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o 2obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o
3bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o 3bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o
4obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o 4obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
5
6obj-y += b53/ 5obj-y += b53/
7obj-y += mv88e6xxx/ 6obj-y += mv88e6xxx/
7obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o
diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c
new file mode 100644
index 000000000000..bc5acc15edbf
--- /dev/null
+++ b/drivers/net/dsa/dsa_loop.c
@@ -0,0 +1,328 @@
1/*
2 * Distributed Switch Architecture loopback driver
3 *
4 * Copyright (C) 2016, Florian Fainelli <f.fainelli@gmail.com>
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
25struct dsa_loop_vlan {
26 u16 members;
27 u16 untagged;
28};
29
30#define DSA_LOOP_VLANS 5
31
32struct 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
40static struct phy_device *phydevs[PHY_MAX_ADDR];
41
42static 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
49static int dsa_loop_setup(struct dsa_switch *ds)
50{
51 dev_dbg(ds->dev, "%s\n", __func__);
52
53 return 0;
54}
55
56static 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
63static 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
73static 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
84static 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
92static 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
98static 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
104static 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
112static 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
130static 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
160static 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
189static 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
226static 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
242static 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
275static 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
284static 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
294static 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
303static 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}
317module_init(dsa_loop_init);
318
319static void __exit dsa_loop_exit(void)
320{
321 mdio_driver_unregister(&dsa_loop_drv);
322 unregister_fixed_phys();
323}
324module_exit(dsa_loop_exit);
325
326MODULE_LICENSE("GPL");
327MODULE_AUTHOR("Florian Fainelli");
328MODULE_DESCRIPTION("DSA loopback driver");
diff --git a/drivers/net/dsa/dsa_loop.h b/drivers/net/dsa/dsa_loop.h
new file mode 100644
index 000000000000..dc396877fc95
--- /dev/null
+++ b/drivers/net/dsa/dsa_loop.h
@@ -0,0 +1,19 @@
1#ifndef __DSA_LOOP_H
2#define __DSA_LOOP_H
3
4struct dsa_chip_data;
5
6struct 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 */
diff --git a/drivers/net/dsa/dsa_loop_bdinfo.c b/drivers/net/dsa/dsa_loop_bdinfo.c
new file mode 100644
index 000000000000..fb8d5dc71013
--- /dev/null
+++ b/drivers/net/dsa/dsa_loop_bdinfo.c
@@ -0,0 +1,34 @@
1#include <linux/kernel.h>
2#include <linux/init.h>
3#include <linux/phy.h>
4#include <net/dsa.h>
5
6#include "dsa_loop.h"
7
8static struct dsa_loop_pdata dsa_loop_pdata = {
9 .cd = {
10 .port_names[0] = "lan1",
11 .port_names[1] = "lan2",
12 .port_names[2] = "lan3",
13 .port_names[3] = "lan4",
14 .port_names[DSA_LOOP_CPU_PORT] = "cpu",
15 },
16 .name = "DSA mockup driver",
17 .enabled_ports = 0x1f,
18 .netdev = "eth0",
19};
20
21static const struct mdio_board_info bdinfo = {
22 .bus_id = "fixed-0",
23 .modalias = "dsa-loop",
24 .mdio_addr = 31,
25 .platform_data = &dsa_loop_pdata,
26};
27
28static int __init dsa_loop_bdinfo_init(void)
29{
30 return mdiobus_register_board_info(&bdinfo, 1);
31}
32arch_initcall(dsa_loop_bdinfo_init)
33
34MODULE_LICENSE("GPL");