summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/mscc/ocelot.c94
-rw-r--r--drivers/net/ethernet/mscc/ocelot.h1
2 files changed, 80 insertions, 15 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index b6254c20f2f0..e815aad8d85e 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -944,10 +944,68 @@ static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
return NULL;
}
+static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
+{
+ if (addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e)
+ return ENTRYTYPE_MACv4;
+ if (addr[0] == 0x33 && addr[1] == 0x33)
+ return ENTRYTYPE_MACv6;
+ return ENTRYTYPE_NORMAL;
+}
+
+static int ocelot_mdb_get_pgid(struct ocelot *ocelot,
+ enum macaccess_entry_type entry_type)
+{
+ int pgid;
+
+ /* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and
+ * 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the
+ * destination mask table (PGID), the destination set is programmed as
+ * part of the entry MAC address.", and the DEST_IDX is set to 0.
+ */
+ if (entry_type == ENTRYTYPE_MACv4 ||
+ entry_type == ENTRYTYPE_MACv6)
+ return 0;
+
+ for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) {
+ struct ocelot_multicast *mc;
+ bool used = false;
+
+ list_for_each_entry(mc, &ocelot->multicast, list) {
+ if (mc->pgid == pgid) {
+ used = true;
+ break;
+ }
+ }
+
+ if (!used)
+ return pgid;
+ }
+
+ return -1;
+}
+
+static void ocelot_encode_ports_to_mdb(unsigned char *addr,
+ struct ocelot_multicast *mc,
+ enum macaccess_entry_type entry_type)
+{
+ memcpy(addr, mc->addr, ETH_ALEN);
+
+ if (entry_type == ENTRYTYPE_MACv4) {
+ addr[0] = 0;
+ addr[1] = mc->ports >> 8;
+ addr[2] = mc->ports & 0xff;
+ } else if (entry_type == ENTRYTYPE_MACv6) {
+ addr[0] = mc->ports >> 8;
+ addr[1] = mc->ports & 0xff;
+ }
+}
+
int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
const struct switchdev_obj_port_mdb *mdb)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
+ enum macaccess_entry_type entry_type;
unsigned char addr[ETH_ALEN];
struct ocelot_multicast *mc;
u16 vid = mdb->vid;
@@ -959,33 +1017,40 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
if (!vid)
vid = ocelot_port->pvid;
+ entry_type = ocelot_classify_mdb(mdb->addr);
+
mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
if (!mc) {
+ int pgid = ocelot_mdb_get_pgid(ocelot, entry_type);
+
+ if (pgid < 0) {
+ dev_err(ocelot->dev,
+ "No more PGIDs available for mdb %pM vid %d\n",
+ mdb->addr, vid);
+ return -ENOSPC;
+ }
+
mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
memcpy(mc->addr, mdb->addr, ETH_ALEN);
mc->vid = vid;
+ mc->pgid = pgid;
list_add_tail(&mc->list, &ocelot->multicast);
new = true;
}
- memcpy(addr, mc->addr, ETH_ALEN);
- addr[0] = 0;
-
if (!new) {
- addr[1] = mc->ports >> 8;
- addr[2] = mc->ports & 0xff;
+ ocelot_encode_ports_to_mdb(addr, mc, entry_type);
ocelot_mact_forget(ocelot, addr, vid);
}
mc->ports |= BIT(port);
- addr[1] = mc->ports >> 8;
- addr[2] = mc->ports & 0xff;
+ ocelot_encode_ports_to_mdb(addr, mc, entry_type);
- return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
+ return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type);
}
EXPORT_SYMBOL(ocelot_port_mdb_add);
@@ -993,6 +1058,7 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
const struct switchdev_obj_port_mdb *mdb)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
+ enum macaccess_entry_type entry_type;
unsigned char addr[ETH_ALEN];
struct ocelot_multicast *mc;
u16 vid = mdb->vid;
@@ -1007,10 +1073,9 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
if (!mc)
return -ENOENT;
- memcpy(addr, mc->addr, ETH_ALEN);
- addr[0] = 0;
- addr[1] = mc->ports >> 8;
- addr[2] = mc->ports & 0xff;
+ entry_type = ocelot_classify_mdb(mdb->addr);
+
+ ocelot_encode_ports_to_mdb(addr, mc, entry_type);
ocelot_mact_forget(ocelot, addr, vid);
mc->ports &= ~BIT(port);
@@ -1020,10 +1085,9 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
return 0;
}
- addr[1] = mc->ports >> 8;
- addr[2] = mc->ports & 0xff;
+ ocelot_encode_ports_to_mdb(addr, mc, entry_type);
- return ocelot_mact_learn(ocelot, 0, addr, vid, ENTRYTYPE_MACv4);
+ return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type);
}
EXPORT_SYMBOL(ocelot_port_mdb_del);
diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
index be4a41646e5e..394362e23c47 100644
--- a/drivers/net/ethernet/mscc/ocelot.h
+++ b/drivers/net/ethernet/mscc/ocelot.h
@@ -46,6 +46,7 @@ struct ocelot_multicast {
unsigned char addr[ETH_ALEN];
u16 vid;
u16 ports;
+ int pgid;
};
struct ocelot_port_tc {