mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-03-19 13:44:03 +00:00
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>
This commit is contained in:
parent
772c3bdad1
commit
98cd1552ea
5 changed files with 390 additions and 1 deletions
|
@ -34,4 +34,12 @@ config NET_DSA_QCA8K
|
||||||
This enables support for the Qualcomm Atheros QCA8K Ethernet
|
This enables support for the Qualcomm Atheros QCA8K Ethernet
|
||||||
switch chips.
|
switch chips.
|
||||||
|
|
||||||
|
config NET_DSA_LOOP
|
||||||
|
tristate "DSA mock-up Ethernet switch chip support"
|
||||||
|
depends on NET_DSA
|
||||||
|
select FIXED_PHY
|
||||||
|
---help---
|
||||||
|
This enables support for a fake mock-up switch chip which
|
||||||
|
exercises the DSA APIs.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
|
@ -2,6 +2,6 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
|
||||||
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o
|
obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o
|
||||||
bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o
|
bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o
|
||||||
obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
|
obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o
|
||||||
|
|
||||||
obj-y += b53/
|
obj-y += b53/
|
||||||
obj-y += mv88e6xxx/
|
obj-y += mv88e6xxx/
|
||||||
|
obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o
|
||||||
|
|
328
drivers/net/dsa/dsa_loop.c
Normal file
328
drivers/net/dsa/dsa_loop.c
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
/*
|
||||||
|
* Distributed Switch Architecture loopback driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016, Florian Fainelli <f.fainelli@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <linux/phy_fixed.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/if_bridge.h>
|
||||||
|
#include <net/switchdev.h>
|
||||||
|
#include <net/dsa.h>
|
||||||
|
|
||||||
|
#include "dsa_loop.h"
|
||||||
|
|
||||||
|
struct dsa_loop_vlan {
|
||||||
|
u16 members;
|
||||||
|
u16 untagged;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DSA_LOOP_VLANS 5
|
||||||
|
|
||||||
|
struct dsa_loop_priv {
|
||||||
|
struct mii_bus *bus;
|
||||||
|
unsigned int port_base;
|
||||||
|
struct dsa_loop_vlan vlans[DSA_LOOP_VLANS];
|
||||||
|
struct net_device *netdev;
|
||||||
|
u16 pvid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct phy_device *phydevs[PHY_MAX_ADDR];
|
||||||
|
|
||||||
|
static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return DSA_TAG_PROTO_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_setup(struct dsa_switch *ds)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum)
|
||||||
|
{
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
struct mii_bus *bus = ps->bus;
|
||||||
|
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return mdiobus_read_nested(bus, ps->port_base + port, regnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
|
||||||
|
int regnum, u16 value)
|
||||||
|
{
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
struct mii_bus *bus = ps->bus;
|
||||||
|
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return mdiobus_write_nested(bus, ps->port_base + port, regnum, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
|
||||||
|
struct net_device *bridge)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||||
|
struct net_device *bridge)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
|
||||||
|
u8 state)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
|
||||||
|
bool vlan_filtering)
|
||||||
|
{
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_obj_port_vlan *vlan,
|
||||||
|
struct switchdev_trans *trans)
|
||||||
|
{
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
struct mii_bus *bus = ps->bus;
|
||||||
|
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
/* Just do a sleeping operation to make lockdep checks effective */
|
||||||
|
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
|
||||||
|
|
||||||
|
if (vlan->vid_end > DSA_LOOP_VLANS)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_obj_port_vlan *vlan,
|
||||||
|
struct switchdev_trans *trans)
|
||||||
|
{
|
||||||
|
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
|
||||||
|
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
struct mii_bus *bus = ps->bus;
|
||||||
|
struct dsa_loop_vlan *vl;
|
||||||
|
u16 vid;
|
||||||
|
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
/* Just do a sleeping operation to make lockdep checks effective */
|
||||||
|
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
|
||||||
|
|
||||||
|
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
|
||||||
|
vl = &ps->vlans[vid];
|
||||||
|
|
||||||
|
vl->members |= BIT(port);
|
||||||
|
if (untagged)
|
||||||
|
vl->untagged |= BIT(port);
|
||||||
|
else
|
||||||
|
vl->untagged &= ~BIT(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pvid)
|
||||||
|
ps->pvid = vid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_obj_port_vlan *vlan)
|
||||||
|
{
|
||||||
|
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
struct mii_bus *bus = ps->bus;
|
||||||
|
struct dsa_loop_vlan *vl;
|
||||||
|
u16 vid, pvid;
|
||||||
|
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
/* Just do a sleeping operation to make lockdep checks effective */
|
||||||
|
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
|
||||||
|
|
||||||
|
for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
|
||||||
|
vl = &ps->vlans[vid];
|
||||||
|
|
||||||
|
vl->members &= ~BIT(port);
|
||||||
|
if (untagged)
|
||||||
|
vl->untagged &= ~BIT(port);
|
||||||
|
|
||||||
|
if (pvid == vid)
|
||||||
|
pvid = 1;
|
||||||
|
}
|
||||||
|
ps->pvid = pvid;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port,
|
||||||
|
struct switchdev_obj_port_vlan *vlan,
|
||||||
|
int (*cb)(struct switchdev_obj *obj))
|
||||||
|
{
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
struct mii_bus *bus = ps->bus;
|
||||||
|
struct dsa_loop_vlan *vl;
|
||||||
|
u16 vid, vid_start = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
dev_dbg(ds->dev, "%s\n", __func__);
|
||||||
|
|
||||||
|
/* Just do a sleeping operation to make lockdep checks effective */
|
||||||
|
mdiobus_read(bus, ps->port_base + port, MII_BMSR);
|
||||||
|
|
||||||
|
for (vid = vid_start; vid < DSA_LOOP_VLANS; vid++) {
|
||||||
|
vl = &ps->vlans[vid];
|
||||||
|
|
||||||
|
if (!(vl->members & BIT(port)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vlan->vid_begin = vlan->vid_end = vid;
|
||||||
|
vlan->flags = 0;
|
||||||
|
|
||||||
|
if (vl->untagged & BIT(port))
|
||||||
|
vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
|
||||||
|
if (ps->pvid == vid)
|
||||||
|
vlan->flags |= BRIDGE_VLAN_INFO_PVID;
|
||||||
|
|
||||||
|
err = cb(&vlan->obj);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dsa_switch_ops dsa_loop_driver = {
|
||||||
|
.get_tag_protocol = dsa_loop_get_protocol,
|
||||||
|
.setup = dsa_loop_setup,
|
||||||
|
.set_addr = dsa_loop_set_addr,
|
||||||
|
.phy_read = dsa_loop_phy_read,
|
||||||
|
.phy_write = dsa_loop_phy_write,
|
||||||
|
.port_bridge_join = dsa_loop_port_bridge_join,
|
||||||
|
.port_bridge_leave = dsa_loop_port_bridge_leave,
|
||||||
|
.port_stp_state_set = dsa_loop_port_stp_state_set,
|
||||||
|
.port_vlan_filtering = dsa_loop_port_vlan_filtering,
|
||||||
|
.port_vlan_prepare = dsa_loop_port_vlan_prepare,
|
||||||
|
.port_vlan_add = dsa_loop_port_vlan_add,
|
||||||
|
.port_vlan_del = dsa_loop_port_vlan_del,
|
||||||
|
.port_vlan_dump = dsa_loop_port_vlan_dump,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dsa_loop_drv_probe(struct mdio_device *mdiodev)
|
||||||
|
{
|
||||||
|
struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data;
|
||||||
|
struct dsa_loop_priv *ps;
|
||||||
|
struct dsa_switch *ds;
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
dev_info(&mdiodev->dev, "%s: 0x%0x\n",
|
||||||
|
pdata->name, pdata->enabled_ports);
|
||||||
|
|
||||||
|
ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS);
|
||||||
|
if (!ds)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL);
|
||||||
|
ps->netdev = dev_get_by_name(&init_net, pdata->netdev);
|
||||||
|
if (!ps->netdev)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
pdata->cd.netdev[DSA_LOOP_CPU_PORT] = &ps->netdev->dev;
|
||||||
|
|
||||||
|
ds->dev = &mdiodev->dev;
|
||||||
|
ds->ops = &dsa_loop_driver;
|
||||||
|
ds->priv = ps;
|
||||||
|
ps->bus = mdiodev->bus;
|
||||||
|
|
||||||
|
dev_set_drvdata(&mdiodev->dev, ds);
|
||||||
|
|
||||||
|
return dsa_register_switch(ds, ds->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dsa_loop_drv_remove(struct mdio_device *mdiodev)
|
||||||
|
{
|
||||||
|
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
|
||||||
|
struct dsa_loop_priv *ps = ds->priv;
|
||||||
|
|
||||||
|
dsa_unregister_switch(ds);
|
||||||
|
dev_put(ps->netdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mdio_driver dsa_loop_drv = {
|
||||||
|
.mdiodrv.driver = {
|
||||||
|
.name = "dsa-loop",
|
||||||
|
},
|
||||||
|
.probe = dsa_loop_drv_probe,
|
||||||
|
.remove = dsa_loop_drv_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2)
|
||||||
|
|
||||||
|
static void unregister_fixed_phys(void)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_FIXED_PHYS; i++)
|
||||||
|
if (phydevs[i])
|
||||||
|
fixed_phy_unregister(phydevs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init dsa_loop_init(void)
|
||||||
|
{
|
||||||
|
struct fixed_phy_status status = {
|
||||||
|
.link = 1,
|
||||||
|
.speed = SPEED_100,
|
||||||
|
.duplex = DUPLEX_FULL,
|
||||||
|
};
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_FIXED_PHYS; i++)
|
||||||
|
phydevs[i] = fixed_phy_register(PHY_POLL, &status, -1, NULL);
|
||||||
|
|
||||||
|
return mdio_driver_register(&dsa_loop_drv);
|
||||||
|
}
|
||||||
|
module_init(dsa_loop_init);
|
||||||
|
|
||||||
|
static void __exit dsa_loop_exit(void)
|
||||||
|
{
|
||||||
|
mdio_driver_unregister(&dsa_loop_drv);
|
||||||
|
unregister_fixed_phys();
|
||||||
|
}
|
||||||
|
module_exit(dsa_loop_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Florian Fainelli");
|
||||||
|
MODULE_DESCRIPTION("DSA loopback driver");
|
19
drivers/net/dsa/dsa_loop.h
Normal file
19
drivers/net/dsa/dsa_loop.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef __DSA_LOOP_H
|
||||||
|
#define __DSA_LOOP_H
|
||||||
|
|
||||||
|
struct dsa_chip_data;
|
||||||
|
|
||||||
|
struct dsa_loop_pdata {
|
||||||
|
/* Must be first, such that dsa_register_switch() can access this
|
||||||
|
* without gory pointer manipulations
|
||||||
|
*/
|
||||||
|
struct dsa_chip_data cd;
|
||||||
|
const char *name;
|
||||||
|
unsigned int enabled_ports;
|
||||||
|
const char *netdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DSA_LOOP_NUM_PORTS 6
|
||||||
|
#define DSA_LOOP_CPU_PORT (DSA_LOOP_NUM_PORTS - 1)
|
||||||
|
|
||||||
|
#endif /* __DSA_LOOP_H */
|
34
drivers/net/dsa/dsa_loop_bdinfo.c
Normal file
34
drivers/net/dsa/dsa_loop_bdinfo.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <net/dsa.h>
|
||||||
|
|
||||||
|
#include "dsa_loop.h"
|
||||||
|
|
||||||
|
static struct dsa_loop_pdata dsa_loop_pdata = {
|
||||||
|
.cd = {
|
||||||
|
.port_names[0] = "lan1",
|
||||||
|
.port_names[1] = "lan2",
|
||||||
|
.port_names[2] = "lan3",
|
||||||
|
.port_names[3] = "lan4",
|
||||||
|
.port_names[DSA_LOOP_CPU_PORT] = "cpu",
|
||||||
|
},
|
||||||
|
.name = "DSA mockup driver",
|
||||||
|
.enabled_ports = 0x1f,
|
||||||
|
.netdev = "eth0",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mdio_board_info bdinfo = {
|
||||||
|
.bus_id = "fixed-0",
|
||||||
|
.modalias = "dsa-loop",
|
||||||
|
.mdio_addr = 31,
|
||||||
|
.platform_data = &dsa_loop_pdata,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dsa_loop_bdinfo_init(void)
|
||||||
|
{
|
||||||
|
return mdiobus_register_board_info(&bdinfo, 1);
|
||||||
|
}
|
||||||
|
arch_initcall(dsa_loop_bdinfo_init)
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
Loading…
Add table
Reference in a new issue