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

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
......@@ -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 */
cmd.index = index;
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,10 +795,10 @@ 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,
const u8 *addr, u16 vid,
struct sja1105_l2_lookup_entry *match,
int *last_unused)
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)
{
int way;
......@@ -818,8 +827,8 @@ 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,
const unsigned char *addr, u16 vid)
int sja1105et_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;
......@@ -827,10 +836,10 @@ 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,
&l2_lookup, &last_unused);
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
* mask? If yes, we need to do nothing. If not, we need
......@@ -874,17 +883,17 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port,
true);
}
static int sja1105_fdb_del(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)
{
struct sja1105_l2_lookup_entry l2_lookup = {0};
struct sja1105_private *priv = ds->priv;
int index, bin, way;
bool keep;
bin = sja1105_fdb_hash(priv, addr, vid);
way = sja1105_is_fdb_entry_in_bin(priv, bin, addr, vid,
&l2_lookup, NULL);
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;
index = sja1105et_fdb_index(bin, way);
......@@ -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);
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 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment