@@ -42,6 +42,7 @@
#include "ptp.h"
#include "serdes.h"
#include "smi.h"
+#include "switchdev.h"
static void assert_reg_lock(struct mv88e6xxx_chip *chip)
{
@@ -2726,18 +2727,25 @@ static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
u16 fdb_flags, struct dsa_db db)
{
+ bool is_dynamic = !!(fdb_flags & DSA_FDB_FLAG_DYNAMIC);
struct mv88e6xxx_chip *chip = ds->priv;
+ u8 state;
int err;
- /* Ignore entries with flags set */
- if (fdb_flags)
- return 0;
+ state = MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC;
+ if (is_dynamic)
+ state = MV88E6XXX_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST;
+ else
+ if (fdb_flags)
+ return 0;
mv88e6xxx_reg_lock(chip);
- err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
- MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
+ err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, state);
mv88e6xxx_reg_unlock(chip);
+ if (is_dynamic && !err)
+ mv88e6xxx_set_fdb_offloaded(ds, port, addr, vid);
+
return err;
}
@@ -432,6 +432,27 @@ static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
spid = entry.state;
+ if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) {
+ unsigned long port = 0;
+ unsigned long portvec = entry.portvec;
+
+ port = find_first_bit(&portvec, MV88E6XXX_MAX_PVT_PORTS);
+ if (port >= MV88E6XXX_MAX_PVT_PORTS) {
+ dev_err(chip->dev,
+ "ATU err: mac: %pM. Port not in portvec: %x\n",
+ entry.mac, entry.portvec);
+ goto out;
+ }
+
+ spid = port;
+ trace_mv88e6xxx_atu_age_out_violation(chip->dev, spid,
+ entry.portvec, entry.mac,
+ fid);
+
+ err = mv88e6xxx_handle_age_out_violation(chip, spid,
+ &entry, fid);
+ }
+
if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) {
trace_mv88e6xxx_atu_member_violation(chip->dev, spid,
entry.portvec, entry.mac,
@@ -1255,7 +1255,11 @@ int mv88e6xxx_port_set_lock(struct mv88e6xxx_chip *chip, int port,
reg &= ~MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
if (locked)
- reg |= MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT;
+ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_LOCKED_PORT |
+ MV88E6XXX_PORT_ASSOC_VECTOR_REFRESH_LOCKED |
+ MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG |
+ MV88E6XXX_PORT_ASSOC_VECTOR_INT_AGE_OUT |
+ MV88E6XXX_PORT_ASSOC_VECTOR_HOLD_AT_1;
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, reg);
}
@@ -12,6 +12,25 @@
#include "global1.h"
#include "switchdev.h"
+void mv88e6xxx_set_fdb_offloaded(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid)
+{
+ struct switchdev_notifier_fdb_info info = {
+ .addr = addr,
+ .vid = vid,
+ .offloaded = true,
+ };
+ struct net_device *brport;
+ struct dsa_port *dp;
+
+ dp = dsa_to_port(ds, port);
+ brport = dsa_port_to_bridge_port(dp);
+
+ if (brport)
+ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
+ brport, &info.info, NULL);
+}
+
struct mv88e6xxx_fid_search_ctx {
u16 fid_search;
u16 vid_found;
@@ -81,3 +100,45 @@ int mv88e6xxx_handle_miss_violation(struct mv88e6xxx_chip *chip, int port,
return err;
}
+
+int mv88e6xxx_handle_age_out_violation(struct mv88e6xxx_chip *chip, int port,
+ struct mv88e6xxx_atu_entry *entry,
+ u16 fid)
+{
+ struct switchdev_notifier_fdb_info info = {
+ .addr = entry->mac,
+ };
+ struct net_device *brport;
+ struct dsa_port *dp;
+ u16 vid;
+ int err;
+
+ err = mv88e6xxx_find_vid(chip, fid, &vid);
+ if (err)
+ return err;
+
+ info.vid = vid;
+ entry->portvec &= ~BIT(port);
+ entry->state = MV88E6XXX_G1_ATU_DATA_STATE_UC_UNUSED;
+ entry->trunk = false;
+
+ mv88e6xxx_reg_lock(chip);
+ err = mv88e6xxx_g1_atu_loadpurge(chip, fid, entry);
+ mv88e6xxx_reg_unlock(chip);
+ if (err)
+ return err;
+
+ dp = dsa_to_port(chip->ds, port);
+
+ rtnl_lock();
+ brport = dsa_port_to_bridge_port(dp);
+ if (!brport) {
+ rtnl_unlock();
+ return -ENODEV;
+ }
+ err = call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
+ brport, &info.info, NULL);
+ rtnl_unlock();
+
+ return err;
+}
@@ -12,8 +12,13 @@
#include "chip.h"
+void mv88e6xxx_set_fdb_offloaded(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid);
int mv88e6xxx_handle_miss_violation(struct mv88e6xxx_chip *chip, int port,
struct mv88e6xxx_atu_entry *entry,
u16 fid);
+int mv88e6xxx_handle_age_out_violation(struct mv88e6xxx_chip *chip, int port,
+ struct mv88e6xxx_atu_entry *entry,
+ u16 fid);
#endif /* _MV88E6XXX_SWITCHDEV_H_ */
@@ -40,6 +40,11 @@ DECLARE_EVENT_CLASS(mv88e6xxx_atu_violation,
__entry->addr, __entry->fid)
);
+DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_age_out_violation,
+ TP_PROTO(const struct device *dev, int spid, u16 portvec,
+ const unsigned char *addr, u16 fid),
+ TP_ARGS(dev, spid, portvec, addr, fid));
+
DEFINE_EVENT(mv88e6xxx_atu_violation, mv88e6xxx_atu_member_violation,
TP_PROTO(const struct device *dev, int spid, u16 portvec,
const unsigned char *addr, u16 fid),