// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 NXP Semiconductors
*
* This is an umbrella module for all network switches that are
* register-compatible with Ocelot and that perform I/O to their host CPU
* through an NPI (Node Processor Interface) Ethernet port.
*/
#include <uapi/linux/if_bridge.h>
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_qsys.h>
#include <soc/mscc/ocelot_sys.h>
#include <soc/mscc/ocelot_dev.h>
#include <soc/mscc/ocelot_ana.h>
#include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot.h>
#include <linux/platform_device.h>
#include <linux/packing.h>
#include <linux/module.h>
#include <linux/of_net.h>
#include <linux/pci.h>
#include <linux/of.h>
#include <net/pkt_sched.h>
#include <net/dsa.h>
#include "felix.h"
static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
return DSA_TAG_PROTO_OCELOT;
}
static int felix_set_ageing_time(struct dsa_switch *ds,
unsigned int ageing_time)
{
struct ocelot *ocelot = ds->priv;
ocelot_set_ageing_time(ocelot, ageing_time);
return 0;
}
static int felix_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
struct ocelot *ocelot = ds->priv;
return ocelot_fdb_dump(ocelot, port, cb, data);
}
static int felix_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid)
{
struct ocelot *ocelot = ds->priv;
return ocelot_fdb_add(ocelot, port, addr, vid);
}
static int felix_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid)
{
struct ocelot *ocelot = ds->priv;
return ocelot_fdb_del(ocelot, port, addr, vid);
}
/* This callback needs to be present */
static int felix_mdb_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb)
{
return 0;
}
static void felix_mdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_mdb_add(ocelot, port, mdb);
}
static int felix_mdb_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb)
{
struct ocelot *ocelot = ds->priv;
return ocelot_port_mdb_del(ocelot, port, mdb);
}
static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
struct ocelot *ocelot = ds->priv;
return ocelot_bridge_stp_state_set(ocelot, port, state);
}
static int felix_bridge_join(struct dsa_switch *ds, int port,
struct net_device *br)
{
struct ocelot *ocelot = ds->priv;
return ocelot_port_bridge_join(ocelot, port, br);
}
static void felix_bridge_leave(struct dsa_switch *ds, int port,
struct net_device *br)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_bridge_leave(ocelot, port, br);
}
/* This callback needs to be present */
static int felix_vlan_prepare(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
return 0;
}
static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
{
struct ocelot *ocelot = ds->priv;
ocelot_port_vlan_filtering(ocelot, port, enabled);
return 0;
}
static void felix_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct ocelot *ocelot = ds->priv;
u16 flags = vlan->flags;
u16 vid;
int err;
if (dsa_is_cpu_port(ds, port))
flags &= ~BRIDGE_VLAN_INFO_UNTAGGED;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
err = ocelot_vlan_add(ocelot, port, vid,
flags & BRIDGE_VLAN_INFO_PVID,
flags & BRIDGE_VLAN_INFO_UNTAGGED);
if (err) {
dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n",
vid, port, err);
return;
}
}
}
static int felix_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct ocelot *ocelot = ds->priv;
u16 vid;
int err;
for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
err = ocelot_vlan_del(ocelot, port, vid);
if (err) {
dev_err(ds->dev,