Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Florian Fainelli and committed by
David S. Miller
98cd1552 772c3bda

+390 -1
+8
drivers/net/dsa/Kconfig
··· 34 34 This enables support for the Qualcomm Atheros QCA8K Ethernet 35 35 switch chips. 36 36 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 + 37 45 endmenu
+1 -1
drivers/net/dsa/Makefile
··· 2 2 obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o 3 3 bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o 4 4 obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o 5 - 6 5 obj-y += b53/ 7 6 obj-y += mv88e6xxx/ 7 + obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o
+328
drivers/net/dsa/dsa_loop.c
··· 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 + 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");
+19
drivers/net/dsa/dsa_loop.h
··· 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 */
+34
drivers/net/dsa/dsa_loop_bdinfo.c
··· 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 + 8 + static 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 + 21 + static 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 + 28 + static int __init dsa_loop_bdinfo_init(void) 29 + { 30 + return mdiobus_register_board_info(&bdinfo, 1); 31 + } 32 + arch_initcall(dsa_loop_bdinfo_init) 33 + 34 + MODULE_LICENSE("GPL");