mirror of
https://github.com/torvalds/linux.git
synced 2024-11-19 02:21:47 +00:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
a6e5afa1bb
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
};
|
||||
|
@ -236,10 +236,20 @@ size_t sja1105pqrs_l2_lookup_entry_packing(void *buf, void *entry_ptr,
|
||||
const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
|
||||
struct sja1105_l2_lookup_entry *entry = entry_ptr;
|
||||
|
||||
/* These are static L2 lookup entries, so the structure
|
||||
* should match UM11040 Table 16/17 definitions when
|
||||
* LOCKEDS is 1.
|
||||
*/
|
||||
if (entry->lockeds) {
|
||||
sja1105_packing(buf, &entry->tsreg, 159, 159, size, op);
|
||||
sja1105_packing(buf, &entry->mirrvlan, 158, 147, size, op);
|
||||
sja1105_packing(buf, &entry->takets, 146, 146, size, op);
|
||||
sja1105_packing(buf, &entry->mirr, 145, 145, size, op);
|
||||
sja1105_packing(buf, &entry->retag, 144, 144, size, op);
|
||||
} else {
|
||||
sja1105_packing(buf, &entry->touched, 159, 159, size, op);
|
||||
sja1105_packing(buf, &entry->age, 158, 144, size, op);
|
||||
}
|
||||
sja1105_packing(buf, &entry->mask_iotag, 143, 143, size, op);
|
||||
sja1105_packing(buf, &entry->mask_vlanid, 142, 131, size, op);
|
||||
sja1105_packing(buf, &entry->mask_macaddr, 130, 83, size, op);
|
||||
sja1105_packing(buf, &entry->iotag, 82, 82, size, op);
|
||||
sja1105_packing(buf, &entry->vlanid, 81, 70, size, op);
|
||||
sja1105_packing(buf, &entry->macaddr, 69, 22, size, op);
|
||||
sja1105_packing(buf, &entry->destports, 21, 17, size, op);
|
||||
|
@ -122,9 +122,35 @@ struct sja1105_l2_lookup_entry {
|
||||
u64 destports;
|
||||
u64 enfport;
|
||||
u64 index;
|
||||
/* P/Q/R/S only */
|
||||
u64 mask_iotag;
|
||||
u64 mask_vlanid;
|
||||
u64 mask_macaddr;
|
||||
u64 iotag;
|
||||
bool lockeds;
|
||||
union {
|
||||
/* LOCKEDS=1: Static FDB entries */
|
||||
struct {
|
||||
u64 tsreg;
|
||||
u64 mirrvlan;
|
||||
u64 takets;
|
||||
u64 mirr;
|
||||
u64 retag;
|
||||
};
|
||||
/* LOCKEDS=0: Dynamically learned FDB entries */
|
||||
struct {
|
||||
u64 touched;
|
||||
u64 age;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct sja1105_l2_lookup_params_entry {
|
||||
u64 start_dynspc; /* P/Q/R/S only */
|
||||
u64 drpnolearn; /* P/Q/R/S only */
|
||||
u64 use_static; /* P/Q/R/S only */
|
||||
u64 owr_dyn; /* P/Q/R/S only */
|
||||
u64 learn_once; /* P/Q/R/S only */
|
||||
u64 maxage; /* Shared */
|
||||
u64 dyn_tbsz; /* E/T only */
|
||||
u64 poly; /* E/T only */
|
||||
|
Loading…
Reference in New Issue
Block a user