Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a6e5afa1 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'FDB-updates-for-SJA1105-DSA-driver'



Vladimir Oltean says:

====================
FDB updates for SJA1105 DSA driver

This patch series adds:

- FDB switchdev support for the second generation of switches (P/Q/R/S).
  I could test/code these now that I got a board with a SJA1105Q.

- Management route support for SJA1105 P/Q/R/S. This is needed to send
  PTP/STP/management frames over the CPU port.

- Logic to hide private DSA VLANs from the 'bridge fdb' commands.

The new FDB code was also tested and still works on SJA1105T.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 014da2a5 93647594
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -55,6 +55,11 @@ struct sja1105_info {
	const struct sja1105_regs *regs;
	int (*reset_cmd)(const void *ctx, const void *data);
	int (*setup_rgmii_delay)(const void *ctx, int port);
	/* Prototypes from include/net/dsa.h */
	int (*fdb_add_cmd)(struct dsa_switch *ds, int port,
			   const unsigned char *addr, u16 vid);
	int (*fdb_del_cmd)(struct dsa_switch *ds, int port,
			   const unsigned char *addr, u16 vid);
	const char *name;
};

@@ -142,7 +147,20 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv,
				 enum sja1105_blk_idx blk_idx,
				 int index, void *entry, bool keep);

u8 sja1105_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid);
enum sja1105_iotag {
	SJA1105_C_TAG = 0, /* Inner VLAN header */
	SJA1105_S_TAG = 1, /* Outer VLAN header */
};

u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid);
int sja1105et_fdb_add(struct dsa_switch *ds, int port,
		      const unsigned char *addr, u16 vid);
int sja1105et_fdb_del(struct dsa_switch *ds, int port,
		      const unsigned char *addr, u16 vid);
int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
			const unsigned char *addr, u16 vid);
int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
			const unsigned char *addr, u16 vid);

/* Common implementations for the static and dynamic configs */
size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr,
+135 −9
Original line number Diff line number Diff line
@@ -35,17 +35,72 @@
#define SJA1105_MAX_DYN_CMD_SIZE				\
	SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD

struct sja1105_dyn_cmd {
	bool search;
	u64 valid;
	u64 rdwrset;
	u64 errors;
	u64 valident;
	u64 index;
};

enum sja1105_hostcmd {
	SJA1105_HOSTCMD_SEARCH = 1,
	SJA1105_HOSTCMD_READ = 2,
	SJA1105_HOSTCMD_WRITE = 3,
	SJA1105_HOSTCMD_INVALIDATE = 4,
};

static void
sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
				  enum packing_op op)
{
	u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
	const int size = SJA1105_SIZE_DYN_CMD;
	u64 lockeds = 0;
	u64 hostcmd;

	sja1105_packing(p, &cmd->valid,    31, 31, size, op);
	sja1105_packing(p, &cmd->rdwrset,  30, 30, size, op);
	sja1105_packing(p, &cmd->errors,   29, 29, size, op);
	sja1105_packing(p, &lockeds,       28, 28, size, op);
	sja1105_packing(p, &cmd->valident, 27, 27, size, op);

	/* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T,
	 * using it to delete a management route was unsupported. UM10944
	 * said about it:
	 *
	 *   In case of a write access with the MGMTROUTE flag set,
	 *   the flag will be ignored. It will always be found cleared
	 *   for read accesses with the MGMTROUTE flag set.
	 *
	 * SJA1105 P/Q/R/S keeps the same behavior w.r.t. VALIDENT, but there
	 * is now another flag called HOSTCMD which does more stuff (quoting
	 * from UM11040):
	 *
	 *   A write request is accepted only when HOSTCMD is set to write host
	 *   or invalid. A read request is accepted only when HOSTCMD is set to
	 *   search host or read host.
	 *
	 * So it is possible to translate a RDWRSET/VALIDENT combination into
	 * HOSTCMD so that we keep the dynamic command API in place, and
	 * at the same time achieve compatibility with the management route
	 * command structure.
	 */
	if (cmd->rdwrset == SPI_READ) {
		if (cmd->search)
			hostcmd = SJA1105_HOSTCMD_SEARCH;
		else
			hostcmd = SJA1105_HOSTCMD_READ;
	} else {
		/* SPI_WRITE */
		if (cmd->valident)
			hostcmd = SJA1105_HOSTCMD_WRITE;
		else
			hostcmd = SJA1105_HOSTCMD_INVALIDATE;
	}
	sja1105_packing(p, &hostcmd, 25, 23, size, op);

	/* Hack - The hardware takes the 'index' field within
	 * struct sja1105_l2_lookup_entry as the index on which this command
	 * will operate. However it will ignore everything else, so 'index'
@@ -54,9 +109,8 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
	 * such that our API doesn't need to ask for a full-blown entry
	 * structure when e.g. a delete is requested.
	 */
	sja1105_packing(buf, &cmd->index, 29, 20,
	sja1105_packing(buf, &cmd->index, 15, 6,
			SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op);
	/* TODO hostcmd */
}

static void
@@ -107,6 +161,36 @@ static size_t sja1105et_mgmt_route_entry_packing(void *buf, void *entry_ptr,
	return size;
}

static void
sja1105pqrs_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
				   enum packing_op op)
{
	u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
	u64 mgmtroute = 1;

	sja1105pqrs_l2_lookup_cmd_packing(buf, cmd, op);
	if (op == PACK)
		sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD);
}

static size_t sja1105pqrs_mgmt_route_entry_packing(void *buf, void *entry_ptr,
						   enum packing_op op)
{
	const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
	struct sja1105_mgmt_entry *entry = entry_ptr;

	/* In P/Q/R/S, enfport got renamed to mgmtvalid, but its purpose
	 * is the same (driver uses it to confirm that frame was sent).
	 * So just keep the name from E/T.
	 */
	sja1105_packing(buf, &entry->tsreg,     71, 71, size, op);
	sja1105_packing(buf, &entry->takets,    70, 70, size, op);
	sja1105_packing(buf, &entry->macaddr,   69, 22, size, op);
	sja1105_packing(buf, &entry->destports, 21, 17, size, op);
	sja1105_packing(buf, &entry->enfport,   16, 16, size, op);
	return size;
}

/* In E/T, entry is at addresses 0x27-0x28. There is a 4 byte gap at 0x29,
 * and command is at 0x2a. Similarly in P/Q/R/S there is a 1 register gap
 * between entry (0x2d, 0x2e) and command (0x30).
@@ -240,6 +324,7 @@ sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,
#define OP_READ		BIT(0)
#define OP_WRITE	BIT(1)
#define OP_DEL		BIT(2)
#define OP_SEARCH	BIT(3)

/* SJA1105E/T: First generation */
struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
@@ -304,14 +389,22 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
	[BLK_IDX_XMII_PARAMS] = {0},
};

/* SJA1105P/Q/R/S: Second generation: TODO */
/* SJA1105P/Q/R/S: Second generation */
struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
	[BLK_IDX_L2_LOOKUP] = {
		.entry_packing = sja1105pqrs_l2_lookup_entry_packing,
		.cmd_packing = sja1105pqrs_l2_lookup_cmd_packing,
		.access = (OP_READ | OP_WRITE | OP_DEL),
		.access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH),
		.max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT,
		.packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD,
		.packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD,
		.addr = 0x24,
	},
	[BLK_IDX_MGMT_ROUTE] = {
		.entry_packing = sja1105pqrs_mgmt_route_entry_packing,
		.cmd_packing = sja1105pqrs_mgmt_route_cmd_packing,
		.access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH),
		.max_entry_count = SJA1105_NUM_PORTS,
		.packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD,
		.addr = 0x24,
	},
	[BLK_IDX_L2_POLICING] = {0},
@@ -359,6 +452,24 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
	[BLK_IDX_XMII_PARAMS] = {0},
};

/* Provides read access to the settings through the dynamic interface
 * of the switch.
 * @blk_idx	is used as key to select from the sja1105_dynamic_table_ops.
 *		The selection is limited by the hardware in respect to which
 *		configuration blocks can be read through the dynamic interface.
 * @index	is used to retrieve a particular table entry. If negative,
 *		(and if the @blk_idx supports the searching operation) a search
 *		is performed by the @entry parameter.
 * @entry	Type-casted to an unpacked structure that holds a table entry
 *		of the type specified in @blk_idx.
 *		Usually an output argument. If @index is negative, then this
 *		argument is used as input/output: it should be pre-populated
 *		with the element to search for. Entries which support the
 *		search operation will have an "index" field (not the @index
 *		argument to this function) and that is where the found index
 *		will be returned (or left unmodified - thus negative - if not
 *		found).
 */
int sja1105_dynamic_config_read(struct sja1105_private *priv,
				enum sja1105_blk_idx blk_idx,
				int index, void *entry)
@@ -375,8 +486,10 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv,

	ops = &priv->info->dyn_ops[blk_idx];

	if (index >= ops->max_entry_count)
	if (index >= 0 && index >= ops->max_entry_count)
		return -ERANGE;
	if (index < 0 && !(ops->access & OP_SEARCH))
		return -EOPNOTSUPP;
	if (!(ops->access & OP_READ))
		return -EOPNOTSUPP;
	if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE)
@@ -388,9 +501,20 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv,

	cmd.valid = true; /* Trigger action on table entry */
	cmd.rdwrset = SPI_READ; /* Action is read */
	if (index < 0) {
		/* Avoid copying a signed negative number to an u64 */
		cmd.index = 0;
		cmd.search = true;
	} else {
		cmd.index = index;
		cmd.search = false;
	}
	cmd.valident = true;
	ops->cmd_packing(packed_buf, &cmd, PACK);

	if (cmd.search)
		ops->entry_packing(packed_buf, entry, PACK);

	/* Send SPI write operation: read config table entry */
	rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE, ops->addr,
					 packed_buf, ops->packed_size);
@@ -416,7 +540,7 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv,
		 * So don't error out in that case.
		 */
		if (!cmd.valident && blk_idx != BLK_IDX_MGMT_ROUTE)
			return -EINVAL;
			return -ENOENT;
		cpu_relax();
	} while (cmd.valid && --retries);

@@ -448,6 +572,8 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv,

	if (index >= ops->max_entry_count)
		return -ERANGE;
	if (index < 0)
		return -ERANGE;
	if (!(ops->access & OP_WRITE))
		return -EOPNOTSUPP;
	if (!keep && !(ops->access & OP_DEL))
@@ -510,7 +636,7 @@ static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly)
 * is also received as argument in the Koopman notation that the switch
 * hardware stores it in.
 */
u8 sja1105_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid)
u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid)
{
	struct sja1105_l2_lookup_params_entry *l2_lookup_params =
		priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS].entries;
+4 −7
Original line number Diff line number Diff line
@@ -7,13 +7,10 @@
#include "sja1105.h"
#include <linux/packing.h>

struct sja1105_dyn_cmd {
	u64 valid;
	u64 rdwrset;
	u64 errors;
	u64 valident;
	u64 index;
};
/* Special index that can be used for sja1105_dynamic_config_read */
#define SJA1105_SEARCH		-1

struct sja1105_dyn_cmd;

struct sja1105_dynamic_table_ops {
	/* This returns size_t just to keep same prototype as the
+169 −17
Original line number Diff line number Diff line
@@ -210,6 +210,8 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
		.maxage = SJA1105_AGEING_TIME_MS(300000),
		/* All entries within a FDB bin are available for learning */
		.dyn_tbsz = SJA1105ET_FDB_BIN_SIZE,
		/* And the P/Q/R/S equivalent setting: */
		.start_dynspc = 0,
		/* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
		.poly = 0x97,
		/* This selects between Independent VLAN Learning (IVL) and
@@ -225,6 +227,13 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
		 * Maybe correlate with no_linklocal_learn from bridge driver?
		 */
		.no_mgmt_learn = true,
		/* P/Q/R/S only */
		.use_static = true,
		/* Dynamically learned FDB entries can overwrite other (older)
		 * dynamic FDB entries
		 */
		.owr_dyn = true,
		.drpnolearn = true,
	};

	table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS];
@@ -786,7 +795,7 @@ static inline int sja1105et_fdb_index(int bin, int way)
	return bin * SJA1105ET_FDB_BIN_SIZE + way;
}

static int sja1105_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin,
static int sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin,
					 const u8 *addr, u16 vid,
					 struct sja1105_l2_lookup_entry *match,
					 int *last_unused)
@@ -818,7 +827,7 @@ static int sja1105_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin,
	return -1;
}

static int sja1105_fdb_add(struct dsa_switch *ds, int port,
int sja1105et_fdb_add(struct dsa_switch *ds, int port,
		      const unsigned char *addr, u16 vid)
{
	struct sja1105_l2_lookup_entry l2_lookup = {0};
@@ -827,9 +836,9 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port,
	int last_unused = -1;
	int bin, way;

	bin = sja1105_fdb_hash(priv, addr, vid);
	bin = sja1105et_fdb_hash(priv, addr, vid);

	way = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid,
	way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid,
					    &l2_lookup, &last_unused);
	if (way >= 0) {
		/* We have an FDB entry. Is our port in the destination
@@ -874,7 +883,7 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port,
					    true);
}

static int sja1105_fdb_del(struct dsa_switch *ds, int port,
int sja1105et_fdb_del(struct dsa_switch *ds, int port,
		      const unsigned char *addr, u16 vid)
{
	struct sja1105_l2_lookup_entry l2_lookup = {0};
@@ -882,8 +891,8 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port,
	int index, bin, way;
	bool keep;

	bin = sja1105_fdb_hash(priv, addr, vid);
	way = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid,
	bin = sja1105et_fdb_hash(priv, addr, vid);
	way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid,
					    &l2_lookup, NULL);
	if (way < 0)
		return 0;
@@ -894,8 +903,8 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port,
	 * need to completely evict the FDB entry.
	 * Otherwise we just write it back.
	 */
	if (l2_lookup.destports & BIT(port))
	l2_lookup.destports &= ~BIT(port);

	if (l2_lookup.destports)
		keep = true;
	else
@@ -905,6 +914,138 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port,
					    index, &l2_lookup, keep);
}

int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
			const unsigned char *addr, u16 vid)
{
	struct sja1105_l2_lookup_entry l2_lookup = {0};
	struct sja1105_private *priv = ds->priv;
	int rc, i;

	/* Search for an existing entry in the FDB table */
	l2_lookup.macaddr = ether_addr_to_u64(addr);
	l2_lookup.vlanid = vid;
	l2_lookup.iotag = SJA1105_S_TAG;
	l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
	l2_lookup.mask_vlanid = VLAN_VID_MASK;
	l2_lookup.mask_iotag = BIT(0);
	l2_lookup.destports = BIT(port);

	rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
					 SJA1105_SEARCH, &l2_lookup);
	if (rc == 0) {
		/* Found and this port is already in the entry's
		 * port mask => job done
		 */
		if (l2_lookup.destports & BIT(port))
			return 0;
		/* l2_lookup.index is populated by the switch in case it
		 * found something.
		 */
		l2_lookup.destports |= BIT(port);
		goto skip_finding_an_index;
	}

	/* Not found, so try to find an unused spot in the FDB.
	 * This is slightly inefficient because the strategy is knock-knock at
	 * every possible position from 0 to 1023.
	 */
	for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
		rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
						 i, NULL);
		if (rc < 0)
			break;
	}
	if (i == SJA1105_MAX_L2_LOOKUP_COUNT) {
		dev_err(ds->dev, "FDB is full, cannot add entry.\n");
		return -EINVAL;
	}
	l2_lookup.index = i;

skip_finding_an_index:
	return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
					    l2_lookup.index, &l2_lookup,
					    true);
}

int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
			const unsigned char *addr, u16 vid)
{
	struct sja1105_l2_lookup_entry l2_lookup = {0};
	struct sja1105_private *priv = ds->priv;
	bool keep;
	int rc;

	l2_lookup.macaddr = ether_addr_to_u64(addr);
	l2_lookup.vlanid = vid;
	l2_lookup.iotag = SJA1105_S_TAG;
	l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0);
	l2_lookup.mask_vlanid = VLAN_VID_MASK;
	l2_lookup.mask_iotag = BIT(0);
	l2_lookup.destports = BIT(port);

	rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
					 SJA1105_SEARCH, &l2_lookup);
	if (rc < 0)
		return 0;

	l2_lookup.destports &= ~BIT(port);

	/* Decide whether we remove just this port from the FDB entry,
	 * or if we remove it completely.
	 */
	if (l2_lookup.destports)
		keep = true;
	else
		keep = false;

	return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
					    l2_lookup.index, &l2_lookup, keep);
}

static int sja1105_fdb_add(struct dsa_switch *ds, int port,
			   const unsigned char *addr, u16 vid)
{
	struct sja1105_private *priv = ds->priv;
	int rc;

	/* Since we make use of VLANs even when the bridge core doesn't tell us
	 * to, translate these FDB entries into the correct dsa_8021q ones.
	 */
	if (!dsa_port_is_vlan_filtering(&ds->ports[port])) {
		unsigned int upstream = dsa_upstream_port(priv->ds, port);
		u16 tx_vid = dsa_8021q_tx_vid(ds, port);
		u16 rx_vid = dsa_8021q_rx_vid(ds, port);

		rc = priv->info->fdb_add_cmd(ds, port, addr, tx_vid);
		if (rc < 0)
			return rc;
		return priv->info->fdb_add_cmd(ds, upstream, addr, rx_vid);
	}
	return priv->info->fdb_add_cmd(ds, port, addr, vid);
}

static int sja1105_fdb_del(struct dsa_switch *ds, int port,
			   const unsigned char *addr, u16 vid)
{
	struct sja1105_private *priv = ds->priv;
	int rc;

	/* Since we make use of VLANs even when the bridge core doesn't tell us
	 * to, translate these FDB entries into the correct dsa_8021q ones.
	 */
	if (!dsa_port_is_vlan_filtering(&ds->ports[port])) {
		unsigned int upstream = dsa_upstream_port(priv->ds, port);
		u16 tx_vid = dsa_8021q_tx_vid(ds, port);
		u16 rx_vid = dsa_8021q_rx_vid(ds, port);

		rc = priv->info->fdb_del_cmd(ds, port, addr, tx_vid);
		if (rc < 0)
			return rc;
		return priv->info->fdb_del_cmd(ds, upstream, addr, rx_vid);
	}
	return priv->info->fdb_del_cmd(ds, port, addr, vid);
}

static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
			    dsa_fdb_dump_cb_t *cb, void *data)
{
@@ -920,7 +1061,7 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
		rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
						 i, &l2_lookup);
		/* No fdb entry at i, not an issue */
		if (rc == -EINVAL)
		if (rc == -ENOENT)
			continue;
		if (rc) {
			dev_err(dev, "Failed to dump FDB: %d\n", rc);
@@ -936,6 +1077,15 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
		if (!(l2_lookup.destports & BIT(port)))
			continue;
		u64_to_ether_addr(l2_lookup.macaddr, macaddr);

		/* We need to hide the dsa_8021q VLAN from the user.
		 * Convert the TX VID into the pvid that is active in
		 * standalone and non-vlan_filtering modes, aka 1.
		 * The RX VID is applied on the CPU port, which is not seen by
		 * the bridge core anyway, so there's nothing to hide.
		 */
		if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
			l2_lookup.vlanid = 1;
		cb(macaddr, l2_lookup.vlanid, false, data);
	}
	return 0;
@@ -1447,6 +1597,8 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
	if (!timeout) {
		/* Clean up the management route so that a follow-up
		 * frame may not match on it by mistake.
		 * This is only hardware supported on P/Q/R/S - on E/T it is
		 * a no-op and we are silently discarding the -EOPNOTSUPP.
		 */
		sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE,
					     slot, &mgmt_route, false);
+12 −0
Original line number Diff line number Diff line
@@ -541,6 +541,8 @@ struct sja1105_info sja1105e_info = {
	.static_ops		= sja1105e_table_ops,
	.dyn_ops		= sja1105et_dyn_ops,
	.reset_cmd		= sja1105et_reset_cmd,
	.fdb_add_cmd		= sja1105et_fdb_add,
	.fdb_del_cmd		= sja1105et_fdb_del,
	.regs			= &sja1105et_regs,
	.name			= "SJA1105E",
};
@@ -550,6 +552,8 @@ struct sja1105_info sja1105t_info = {
	.static_ops		= sja1105t_table_ops,
	.dyn_ops		= sja1105et_dyn_ops,
	.reset_cmd		= sja1105et_reset_cmd,
	.fdb_add_cmd		= sja1105et_fdb_add,
	.fdb_del_cmd		= sja1105et_fdb_del,
	.regs			= &sja1105et_regs,
	.name			= "SJA1105T",
};
@@ -559,6 +563,8 @@ struct sja1105_info sja1105p_info = {
	.static_ops		= sja1105p_table_ops,
	.dyn_ops		= sja1105pqrs_dyn_ops,
	.reset_cmd		= sja1105pqrs_reset_cmd,
	.fdb_add_cmd		= sja1105pqrs_fdb_add,
	.fdb_del_cmd		= sja1105pqrs_fdb_del,
	.regs			= &sja1105pqrs_regs,
	.name			= "SJA1105P",
};
@@ -568,6 +574,8 @@ struct sja1105_info sja1105q_info = {
	.static_ops		= sja1105q_table_ops,
	.dyn_ops		= sja1105pqrs_dyn_ops,
	.reset_cmd		= sja1105pqrs_reset_cmd,
	.fdb_add_cmd		= sja1105pqrs_fdb_add,
	.fdb_del_cmd		= sja1105pqrs_fdb_del,
	.regs			= &sja1105pqrs_regs,
	.name			= "SJA1105Q",
};
@@ -577,6 +585,8 @@ struct sja1105_info sja1105r_info = {
	.static_ops		= sja1105r_table_ops,
	.dyn_ops		= sja1105pqrs_dyn_ops,
	.reset_cmd		= sja1105pqrs_reset_cmd,
	.fdb_add_cmd		= sja1105pqrs_fdb_add,
	.fdb_del_cmd		= sja1105pqrs_fdb_del,
	.regs			= &sja1105pqrs_regs,
	.name			= "SJA1105R",
};
@@ -587,5 +597,7 @@ struct sja1105_info sja1105s_info = {
	.dyn_ops		= sja1105pqrs_dyn_ops,
	.regs			= &sja1105pqrs_regs,
	.reset_cmd		= sja1105pqrs_reset_cmd,
	.fdb_add_cmd		= sja1105pqrs_fdb_add,
	.fdb_del_cmd		= sja1105pqrs_fdb_del,
	.name			= "SJA1105S",
};
Loading