Merge branch 'FDB-VLAN-and-PTP-fixes-for-SJA1105-DSA'
Vladimir Oltean says: ==================== FDB, VLAN and PTP fixes for SJA1105 DSA This patchset is an assortment of fixes for the net-next version of the sja1105 DSA driver: - Avoid a kernel panic when the driver fails to probe or unregisters - Finish Arnd Bermann's idea of compiling PTP support as part of the main DSA driver and not separately - Better handling of initial port-based VLAN as well as VLANs for dsa_8021q FDB entries - Fix address learning for the SJA1105 P/Q/R/S family - Make static FDB entries persistent across switch resets - Fix reporting of statically-added FDB entries in 'bridge fdb show' ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
5b1bf3f644
|
@ -10,5 +10,5 @@ sja1105-objs := \
|
||||||
sja1105_dynamic_config.o \
|
sja1105_dynamic_config.o \
|
||||||
|
|
||||||
ifdef CONFIG_NET_DSA_SJA1105_PTP
|
ifdef CONFIG_NET_DSA_SJA1105_PTP
|
||||||
obj-$(CONFIG_NET_DSA_SJA1105) += sja1105_ptp.o
|
sja1105-objs += sja1105_ptp.o
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -3,6 +3,98 @@
|
||||||
*/
|
*/
|
||||||
#include "sja1105.h"
|
#include "sja1105.h"
|
||||||
|
|
||||||
|
/* In the dynamic configuration interface, the switch exposes a register-like
|
||||||
|
* view of some of the static configuration tables.
|
||||||
|
* Many times the field organization of the dynamic tables is abbreviated (not
|
||||||
|
* all fields are dynamically reconfigurable) and different from the static
|
||||||
|
* ones, but the key reason for having it is that we can spare a switch reset
|
||||||
|
* for settings that can be changed dynamically.
|
||||||
|
*
|
||||||
|
* This file creates a per-switch-family abstraction called
|
||||||
|
* struct sja1105_dynamic_table_ops and two operations that work with it:
|
||||||
|
* - sja1105_dynamic_config_write
|
||||||
|
* - sja1105_dynamic_config_read
|
||||||
|
*
|
||||||
|
* Compared to the struct sja1105_table_ops from sja1105_static_config.c,
|
||||||
|
* the dynamic accessors work with a compound buffer:
|
||||||
|
*
|
||||||
|
* packed_buf
|
||||||
|
*
|
||||||
|
* |
|
||||||
|
* V
|
||||||
|
* +-----------------------------------------+------------------+
|
||||||
|
* | ENTRY BUFFER | COMMAND BUFFER |
|
||||||
|
* +-----------------------------------------+------------------+
|
||||||
|
*
|
||||||
|
* <----------------------- packed_size ------------------------>
|
||||||
|
*
|
||||||
|
* The ENTRY BUFFER may or may not have the same layout, or size, as its static
|
||||||
|
* configuration table entry counterpart. When it does, the same packing
|
||||||
|
* function is reused (bar exceptional cases - see
|
||||||
|
* sja1105pqrs_dyn_l2_lookup_entry_packing).
|
||||||
|
*
|
||||||
|
* The reason for the COMMAND BUFFER being at the end is to be able to send
|
||||||
|
* a dynamic write command through a single SPI burst. By the time the switch
|
||||||
|
* reacts to the command, the ENTRY BUFFER is already populated with the data
|
||||||
|
* sent by the core.
|
||||||
|
*
|
||||||
|
* The COMMAND BUFFER is always SJA1105_SIZE_DYN_CMD bytes (one 32-bit word) in
|
||||||
|
* size.
|
||||||
|
*
|
||||||
|
* Sometimes the ENTRY BUFFER does not really exist (when the number of fields
|
||||||
|
* that can be reconfigured is small), then the switch repurposes some of the
|
||||||
|
* unused 32 bits of the COMMAND BUFFER to hold ENTRY data.
|
||||||
|
*
|
||||||
|
* The key members of struct sja1105_dynamic_table_ops are:
|
||||||
|
* - .entry_packing: A function that deals with packing an ENTRY structure
|
||||||
|
* into an SPI buffer, or retrieving an ENTRY structure
|
||||||
|
* from one.
|
||||||
|
* The @packed_buf pointer it's given does always point to
|
||||||
|
* the ENTRY portion of the buffer.
|
||||||
|
* - .cmd_packing: A function that deals with packing/unpacking the COMMAND
|
||||||
|
* structure to/from the SPI buffer.
|
||||||
|
* It is given the same @packed_buf pointer as .entry_packing,
|
||||||
|
* so most of the time, the @packed_buf points *behind* the
|
||||||
|
* COMMAND offset inside the buffer.
|
||||||
|
* To access the COMMAND portion of the buffer, the function
|
||||||
|
* knows its correct offset.
|
||||||
|
* Giving both functions the same pointer is handy because in
|
||||||
|
* extreme cases (see sja1105pqrs_dyn_l2_lookup_entry_packing)
|
||||||
|
* the .entry_packing is able to jump to the COMMAND portion,
|
||||||
|
* or vice-versa (sja1105pqrs_l2_lookup_cmd_packing).
|
||||||
|
* - .access: A bitmap of:
|
||||||
|
* OP_READ: Set if the hardware manual marks the ENTRY portion of the
|
||||||
|
* dynamic configuration table buffer as R (readable) after
|
||||||
|
* an SPI read command (the switch will populate the buffer).
|
||||||
|
* OP_WRITE: Set if the manual marks the ENTRY portion of the dynamic
|
||||||
|
* table buffer as W (writable) after an SPI write command
|
||||||
|
* (the switch will read the fields provided in the buffer).
|
||||||
|
* OP_DEL: Set if the manual says the VALIDENT bit is supported in the
|
||||||
|
* COMMAND portion of this dynamic config buffer (i.e. the
|
||||||
|
* specified entry can be invalidated through a SPI write
|
||||||
|
* command).
|
||||||
|
* OP_SEARCH: Set if the manual says that the index of an entry can
|
||||||
|
* be retrieved in the COMMAND portion of the buffer based
|
||||||
|
* on its ENTRY portion, as a result of a SPI write command.
|
||||||
|
* Only the TCAM-based FDB table on SJA1105 P/Q/R/S supports
|
||||||
|
* this.
|
||||||
|
* - .max_entry_count: The number of entries, counting from zero, that can be
|
||||||
|
* reconfigured through the dynamic interface. If a static
|
||||||
|
* table can be reconfigured at all dynamically, this
|
||||||
|
* number always matches the maximum number of supported
|
||||||
|
* static entries.
|
||||||
|
* - .packed_size: The length in bytes of the compound ENTRY + COMMAND BUFFER.
|
||||||
|
* Note that sometimes the compound buffer may contain holes in
|
||||||
|
* it (see sja1105_vlan_lookup_cmd_packing). The @packed_buf is
|
||||||
|
* contiguous however, so @packed_size includes any unused
|
||||||
|
* bytes.
|
||||||
|
* - .addr: The base SPI address at which the buffer must be written to the
|
||||||
|
* switch's memory. When looking at the hardware manual, this must
|
||||||
|
* always match the lowest documented address for the ENTRY, and not
|
||||||
|
* that of the COMMAND, since the other 32-bit words will follow along
|
||||||
|
* at the correct addresses.
|
||||||
|
*/
|
||||||
|
|
||||||
#define SJA1105_SIZE_DYN_CMD 4
|
#define SJA1105_SIZE_DYN_CMD 4
|
||||||
|
|
||||||
#define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \
|
#define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \
|
||||||
|
@ -57,13 +149,11 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
||||||
{
|
{
|
||||||
u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
|
u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
|
||||||
const int size = SJA1105_SIZE_DYN_CMD;
|
const int size = SJA1105_SIZE_DYN_CMD;
|
||||||
u64 lockeds = 0;
|
|
||||||
u64 hostcmd;
|
u64 hostcmd;
|
||||||
|
|
||||||
sja1105_packing(p, &cmd->valid, 31, 31, size, op);
|
sja1105_packing(p, &cmd->valid, 31, 31, size, op);
|
||||||
sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op);
|
sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op);
|
||||||
sja1105_packing(p, &cmd->errors, 29, 29, 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);
|
sja1105_packing(p, &cmd->valident, 27, 27, size, op);
|
||||||
|
|
||||||
/* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T,
|
/* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T,
|
||||||
|
@ -113,6 +203,64 @@ sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
||||||
SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op);
|
SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The switch is so retarded that it makes our command/entry abstraction
|
||||||
|
* crumble apart.
|
||||||
|
*
|
||||||
|
* On P/Q/R/S, the switch tries to say whether a FDB entry
|
||||||
|
* is statically programmed or dynamically learned via a flag called LOCKEDS.
|
||||||
|
* The hardware manual says about this fiels:
|
||||||
|
*
|
||||||
|
* On write will specify the format of ENTRY.
|
||||||
|
* On read the flag will be found cleared at times the VALID flag is found
|
||||||
|
* set. The flag will also be found cleared in response to a read having the
|
||||||
|
* MGMTROUTE flag set. In response to a read with the MGMTROUTE flag
|
||||||
|
* cleared, the flag be set if the most recent access operated on an entry
|
||||||
|
* that was either loaded by configuration or through dynamic reconfiguration
|
||||||
|
* (as opposed to automatically learned entries).
|
||||||
|
*
|
||||||
|
* The trouble with this flag is that it's part of the *command* to access the
|
||||||
|
* dynamic interface, and not part of the *entry* retrieved from it.
|
||||||
|
* Otherwise said, for a sja1105_dynamic_config_read, LOCKEDS is supposed to be
|
||||||
|
* an output from the switch into the command buffer, and for a
|
||||||
|
* sja1105_dynamic_config_write, the switch treats LOCKEDS as an input
|
||||||
|
* (hence we can write either static, or automatically learned entries, from
|
||||||
|
* the core).
|
||||||
|
* But the manual contradicts itself in the last phrase where it says that on
|
||||||
|
* read, LOCKEDS will be set to 1 for all FDB entries written through the
|
||||||
|
* dynamic interface (therefore, the value of LOCKEDS from the
|
||||||
|
* sja1105_dynamic_config_write is not really used for anything, it'll store a
|
||||||
|
* 1 anyway).
|
||||||
|
* This means you can't really write a FDB entry with LOCKEDS=0 (automatically
|
||||||
|
* learned) into the switch, which kind of makes sense.
|
||||||
|
* As for reading through the dynamic interface, it doesn't make too much sense
|
||||||
|
* to put LOCKEDS into the command, since the switch will inevitably have to
|
||||||
|
* ignore it (otherwise a command would be like "read the FDB entry 123, but
|
||||||
|
* only if it's dynamically learned" <- well how am I supposed to know?) and
|
||||||
|
* just use it as an output buffer for its findings. But guess what... that's
|
||||||
|
* what the entry buffer is for!
|
||||||
|
* Unfortunately, what really breaks this abstraction is the fact that it
|
||||||
|
* wasn't designed having the fact in mind that the switch can output
|
||||||
|
* entry-related data as writeback through the command buffer.
|
||||||
|
* However, whether a FDB entry is statically or dynamically learned *is* part
|
||||||
|
* of the entry and not the command data, no matter what the switch thinks.
|
||||||
|
* In order to do that, we'll need to wrap around the
|
||||||
|
* sja1105pqrs_l2_lookup_entry_packing from sja1105_static_config.c, and take
|
||||||
|
* a peek outside of the caller-supplied @buf (the entry buffer), to reach the
|
||||||
|
* command buffer.
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
sja1105pqrs_dyn_l2_lookup_entry_packing(void *buf, void *entry_ptr,
|
||||||
|
enum packing_op op)
|
||||||
|
{
|
||||||
|
struct sja1105_l2_lookup_entry *entry = entry_ptr;
|
||||||
|
u8 *cmd = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
|
||||||
|
const int size = SJA1105_SIZE_DYN_CMD;
|
||||||
|
|
||||||
|
sja1105_packing(cmd, &entry->lockeds, 28, 28, size, op);
|
||||||
|
|
||||||
|
return sja1105pqrs_l2_lookup_entry_packing(buf, entry_ptr, op);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
||||||
enum packing_op op)
|
enum packing_op op)
|
||||||
|
@ -393,7 +541,7 @@ struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
|
||||||
/* SJA1105P/Q/R/S: Second generation */
|
/* SJA1105P/Q/R/S: Second generation */
|
||||||
struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
|
struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
|
||||||
[BLK_IDX_L2_LOOKUP] = {
|
[BLK_IDX_L2_LOOKUP] = {
|
||||||
.entry_packing = sja1105pqrs_l2_lookup_entry_packing,
|
.entry_packing = sja1105pqrs_dyn_l2_lookup_entry_packing,
|
||||||
.cmd_packing = sja1105pqrs_l2_lookup_cmd_packing,
|
.cmd_packing = sja1105pqrs_l2_lookup_cmd_packing,
|
||||||
.access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH),
|
.access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH),
|
||||||
.max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT,
|
.max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT,
|
||||||
|
|
|
@ -80,7 +80,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
|
||||||
.maxage = 0xFF,
|
.maxage = 0xFF,
|
||||||
/* Internal VLAN (pvid) to apply to untagged ingress */
|
/* Internal VLAN (pvid) to apply to untagged ingress */
|
||||||
.vlanprio = 0,
|
.vlanprio = 0,
|
||||||
.vlanid = 0,
|
.vlanid = 1,
|
||||||
.ing_mirr = false,
|
.ing_mirr = false,
|
||||||
.egr_mirr = false,
|
.egr_mirr = false,
|
||||||
/* Don't drop traffic with other EtherType than ETH_P_IP */
|
/* Don't drop traffic with other EtherType than ETH_P_IP */
|
||||||
|
@ -203,6 +203,7 @@ static int sja1105_init_static_fdb(struct sja1105_private *priv)
|
||||||
static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
|
static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
|
||||||
{
|
{
|
||||||
struct sja1105_table *table;
|
struct sja1105_table *table;
|
||||||
|
u64 max_fdb_entries = SJA1105_MAX_L2_LOOKUP_COUNT / SJA1105_NUM_PORTS;
|
||||||
struct sja1105_l2_lookup_params_entry default_l2_lookup_params = {
|
struct sja1105_l2_lookup_params_entry default_l2_lookup_params = {
|
||||||
/* Learned FDB entries are forgotten after 300 seconds */
|
/* Learned FDB entries are forgotten after 300 seconds */
|
||||||
.maxage = SJA1105_AGEING_TIME_MS(300000),
|
.maxage = SJA1105_AGEING_TIME_MS(300000),
|
||||||
|
@ -210,6 +211,8 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
|
||||||
.dyn_tbsz = SJA1105ET_FDB_BIN_SIZE,
|
.dyn_tbsz = SJA1105ET_FDB_BIN_SIZE,
|
||||||
/* And the P/Q/R/S equivalent setting: */
|
/* And the P/Q/R/S equivalent setting: */
|
||||||
.start_dynspc = 0,
|
.start_dynspc = 0,
|
||||||
|
.maxaddrp = {max_fdb_entries, max_fdb_entries, max_fdb_entries,
|
||||||
|
max_fdb_entries, max_fdb_entries, },
|
||||||
/* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
|
/* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */
|
||||||
.poly = 0x97,
|
.poly = 0x97,
|
||||||
/* This selects between Independent VLAN Learning (IVL) and
|
/* This selects between Independent VLAN Learning (IVL) and
|
||||||
|
@ -264,20 +267,15 @@ static int sja1105_init_static_vlan(struct sja1105_private *priv)
|
||||||
.vmemb_port = 0,
|
.vmemb_port = 0,
|
||||||
.vlan_bc = 0,
|
.vlan_bc = 0,
|
||||||
.tag_port = 0,
|
.tag_port = 0,
|
||||||
.vlanid = 0,
|
.vlanid = 1,
|
||||||
};
|
};
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP];
|
table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP];
|
||||||
|
|
||||||
/* The static VLAN table will only contain the initial pvid of 0.
|
/* The static VLAN table will only contain the initial pvid of 1.
|
||||||
* All other VLANs are to be configured through dynamic entries,
|
* All other VLANs are to be configured through dynamic entries,
|
||||||
* and kept in the static configuration table as backing memory.
|
* and kept in the static configuration table as backing memory.
|
||||||
* The pvid of 0 is sufficient to pass traffic while the ports are
|
|
||||||
* standalone and when vlan_filtering is disabled. When filtering
|
|
||||||
* gets enabled, the switchdev core sets up the VLAN ID 1 and sets
|
|
||||||
* it as the new pvid. Actually 'pvid 1' still comes up in 'bridge
|
|
||||||
* vlan' even when vlan_filtering is off, but it has no effect.
|
|
||||||
*/
|
*/
|
||||||
if (table->entry_count) {
|
if (table->entry_count) {
|
||||||
kfree(table->entries);
|
kfree(table->entries);
|
||||||
|
@ -291,7 +289,7 @@ static int sja1105_init_static_vlan(struct sja1105_private *priv)
|
||||||
|
|
||||||
table->entry_count = 1;
|
table->entry_count = 1;
|
||||||
|
|
||||||
/* VLAN ID 0: all DT-defined ports are members; no restrictions on
|
/* VLAN 1: all DT-defined ports are members; no restrictions on
|
||||||
* forwarding; always transmit priority-tagged frames as untagged.
|
* forwarding; always transmit priority-tagged frames as untagged.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
|
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
|
||||||
|
@ -818,6 +816,77 @@ static void sja1105_phylink_validate(struct dsa_switch *ds, int port,
|
||||||
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
__ETHTOOL_LINK_MODE_MASK_NBITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sja1105_find_static_fdb_entry(struct sja1105_private *priv, int port,
|
||||||
|
const struct sja1105_l2_lookup_entry *requested)
|
||||||
|
{
|
||||||
|
struct sja1105_l2_lookup_entry *l2_lookup;
|
||||||
|
struct sja1105_table *table;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP];
|
||||||
|
l2_lookup = table->entries;
|
||||||
|
|
||||||
|
for (i = 0; i < table->entry_count; i++)
|
||||||
|
if (l2_lookup[i].macaddr == requested->macaddr &&
|
||||||
|
l2_lookup[i].vlanid == requested->vlanid &&
|
||||||
|
l2_lookup[i].destports & BIT(port))
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We want FDB entries added statically through the bridge command to persist
|
||||||
|
* across switch resets, which are a common thing during normal SJA1105
|
||||||
|
* operation. So we have to back them up in the static configuration tables
|
||||||
|
* and hence apply them on next static config upload... yay!
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sja1105_static_fdb_change(struct sja1105_private *priv, int port,
|
||||||
|
const struct sja1105_l2_lookup_entry *requested,
|
||||||
|
bool keep)
|
||||||
|
{
|
||||||
|
struct sja1105_l2_lookup_entry *l2_lookup;
|
||||||
|
struct sja1105_table *table;
|
||||||
|
int rc, match;
|
||||||
|
|
||||||
|
table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP];
|
||||||
|
|
||||||
|
match = sja1105_find_static_fdb_entry(priv, port, requested);
|
||||||
|
if (match < 0) {
|
||||||
|
/* Can't delete a missing entry. */
|
||||||
|
if (!keep)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* No match => new entry */
|
||||||
|
rc = sja1105_table_resize(table, table->entry_count + 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
match = table->entry_count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assign pointer after the resize (it may be new memory) */
|
||||||
|
l2_lookup = table->entries;
|
||||||
|
|
||||||
|
/* We have a match.
|
||||||
|
* If the job was to add this FDB entry, it's already done (mostly
|
||||||
|
* anyway, since the port forwarding mask may have changed, case in
|
||||||
|
* which we update it).
|
||||||
|
* Otherwise we have to delete it.
|
||||||
|
*/
|
||||||
|
if (keep) {
|
||||||
|
l2_lookup[match] = *requested;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To remove, the strategy is to overwrite the element with
|
||||||
|
* the last one, and then reduce the array size by 1
|
||||||
|
*/
|
||||||
|
l2_lookup[match] = l2_lookup[table->entry_count - 1];
|
||||||
|
return sja1105_table_resize(table, table->entry_count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* First-generation switches have a 4-way set associative TCAM that
|
/* First-generation switches have a 4-way set associative TCAM that
|
||||||
* holds the FDB entries. An FDB index spans from 0 to 1023 and is comprised of
|
* holds the FDB entries. An FDB index spans from 0 to 1023 and is comprised of
|
||||||
* a "bin" (grouping of 4 entries) and a "way" (an entry within a bin).
|
* a "bin" (grouping of 4 entries) and a "way" (an entry within a bin).
|
||||||
|
@ -868,7 +937,7 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
struct device *dev = ds->dev;
|
struct device *dev = ds->dev;
|
||||||
int last_unused = -1;
|
int last_unused = -1;
|
||||||
int bin, way;
|
int bin, way, rc;
|
||||||
|
|
||||||
bin = sja1105et_fdb_hash(priv, addr, vid);
|
bin = sja1105et_fdb_hash(priv, addr, vid);
|
||||||
|
|
||||||
|
@ -912,9 +981,13 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port,
|
||||||
}
|
}
|
||||||
l2_lookup.index = sja1105et_fdb_index(bin, way);
|
l2_lookup.index = sja1105et_fdb_index(bin, way);
|
||||||
|
|
||||||
return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
l2_lookup.index, &l2_lookup,
|
l2_lookup.index, &l2_lookup,
|
||||||
true);
|
true);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return sja1105_static_fdb_change(priv, port, &l2_lookup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sja1105et_fdb_del(struct dsa_switch *ds, int port,
|
int sja1105et_fdb_del(struct dsa_switch *ds, int port,
|
||||||
|
@ -922,7 +995,7 @@ int sja1105et_fdb_del(struct dsa_switch *ds, int port,
|
||||||
{
|
{
|
||||||
struct sja1105_l2_lookup_entry l2_lookup = {0};
|
struct sja1105_l2_lookup_entry l2_lookup = {0};
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
int index, bin, way;
|
int index, bin, way, rc;
|
||||||
bool keep;
|
bool keep;
|
||||||
|
|
||||||
bin = sja1105et_fdb_hash(priv, addr, vid);
|
bin = sja1105et_fdb_hash(priv, addr, vid);
|
||||||
|
@ -944,8 +1017,12 @@ int sja1105et_fdb_del(struct dsa_switch *ds, int port,
|
||||||
else
|
else
|
||||||
keep = false;
|
keep = false;
|
||||||
|
|
||||||
return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
index, &l2_lookup, keep);
|
index, &l2_lookup, keep);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return sja1105_static_fdb_change(priv, port, &l2_lookup, keep);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
|
int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
|
||||||
|
@ -993,12 +1070,17 @@ int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
|
||||||
dev_err(ds->dev, "FDB is full, cannot add entry.\n");
|
dev_err(ds->dev, "FDB is full, cannot add entry.\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
l2_lookup.lockeds = true;
|
||||||
l2_lookup.index = i;
|
l2_lookup.index = i;
|
||||||
|
|
||||||
skip_finding_an_index:
|
skip_finding_an_index:
|
||||||
return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
l2_lookup.index, &l2_lookup,
|
l2_lookup.index, &l2_lookup,
|
||||||
true);
|
true);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return sja1105_static_fdb_change(priv, port, &l2_lookup, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
|
int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
|
||||||
|
@ -1032,52 +1114,72 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
|
||||||
else
|
else
|
||||||
keep = false;
|
keep = false;
|
||||||
|
|
||||||
return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP,
|
||||||
l2_lookup.index, &l2_lookup, keep);
|
l2_lookup.index, &l2_lookup, keep);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return sja1105_static_fdb_change(priv, port, &l2_lookup, keep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sja1105_fdb_add(struct dsa_switch *ds, int port,
|
static int sja1105_fdb_add(struct dsa_switch *ds, int port,
|
||||||
const unsigned char *addr, u16 vid)
|
const unsigned char *addr, u16 vid)
|
||||||
{
|
{
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
int rc;
|
u16 rx_vid, tx_vid;
|
||||||
|
int rc, i;
|
||||||
|
|
||||||
|
if (dsa_port_is_vlan_filtering(&ds->ports[port]))
|
||||||
|
return priv->info->fdb_add_cmd(ds, port, addr, vid);
|
||||||
|
|
||||||
/* Since we make use of VLANs even when the bridge core doesn't tell us
|
/* 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.
|
* to, translate these FDB entries into the correct dsa_8021q ones.
|
||||||
|
* The basic idea (also repeats for removal below) is:
|
||||||
|
* - Each of the other front-panel ports needs to be able to forward a
|
||||||
|
* pvid-tagged (aka tagged with their rx_vid) frame that matches this
|
||||||
|
* DMAC.
|
||||||
|
* - The CPU port (aka the tx_vid of this port) needs to be able to
|
||||||
|
* send a frame matching this DMAC to the specified port.
|
||||||
|
* For a better picture see net/dsa/tag_8021q.c.
|
||||||
*/
|
*/
|
||||||
if (!dsa_port_is_vlan_filtering(&ds->ports[port])) {
|
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
|
||||||
unsigned int upstream = dsa_upstream_port(priv->ds, port);
|
if (i == port)
|
||||||
u16 tx_vid = dsa_8021q_tx_vid(ds, port);
|
continue;
|
||||||
u16 rx_vid = dsa_8021q_rx_vid(ds, port);
|
if (i == dsa_upstream_port(priv->ds, port))
|
||||||
|
continue;
|
||||||
|
|
||||||
rc = priv->info->fdb_add_cmd(ds, port, addr, tx_vid);
|
rx_vid = dsa_8021q_rx_vid(ds, i);
|
||||||
|
rc = priv->info->fdb_add_cmd(ds, port, addr, rx_vid);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
return priv->info->fdb_add_cmd(ds, upstream, addr, rx_vid);
|
|
||||||
}
|
}
|
||||||
return priv->info->fdb_add_cmd(ds, port, addr, vid);
|
tx_vid = dsa_8021q_tx_vid(ds, port);
|
||||||
|
return priv->info->fdb_add_cmd(ds, port, addr, tx_vid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sja1105_fdb_del(struct dsa_switch *ds, int port,
|
static int sja1105_fdb_del(struct dsa_switch *ds, int port,
|
||||||
const unsigned char *addr, u16 vid)
|
const unsigned char *addr, u16 vid)
|
||||||
{
|
{
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
int rc;
|
u16 rx_vid, tx_vid;
|
||||||
|
int rc, i;
|
||||||
|
|
||||||
/* Since we make use of VLANs even when the bridge core doesn't tell us
|
if (dsa_port_is_vlan_filtering(&ds->ports[port]))
|
||||||
* to, translate these FDB entries into the correct dsa_8021q ones.
|
return priv->info->fdb_del_cmd(ds, port, addr, vid);
|
||||||
*/
|
|
||||||
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);
|
for (i = 0; i < SJA1105_NUM_PORTS; i++) {
|
||||||
|
if (i == port)
|
||||||
|
continue;
|
||||||
|
if (i == dsa_upstream_port(priv->ds, port))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rx_vid = dsa_8021q_rx_vid(ds, i);
|
||||||
|
rc = priv->info->fdb_del_cmd(ds, port, addr, rx_vid);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
return priv->info->fdb_del_cmd(ds, upstream, addr, rx_vid);
|
|
||||||
}
|
}
|
||||||
return priv->info->fdb_del_cmd(ds, port, addr, vid);
|
tx_vid = dsa_8021q_tx_vid(ds, port);
|
||||||
|
return priv->info->fdb_del_cmd(ds, port, addr, tx_vid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
|
static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
|
||||||
|
@ -1085,8 +1187,12 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
|
||||||
{
|
{
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
struct device *dev = ds->dev;
|
struct device *dev = ds->dev;
|
||||||
|
u16 rx_vid, tx_vid;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
rx_vid = dsa_8021q_rx_vid(ds, port);
|
||||||
|
tx_vid = dsa_8021q_tx_vid(ds, port);
|
||||||
|
|
||||||
for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
|
for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
|
||||||
struct sja1105_l2_lookup_entry l2_lookup = {0};
|
struct sja1105_l2_lookup_entry l2_lookup = {0};
|
||||||
u8 macaddr[ETH_ALEN];
|
u8 macaddr[ETH_ALEN];
|
||||||
|
@ -1112,15 +1218,40 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
|
||||||
continue;
|
continue;
|
||||||
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
|
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
|
||||||
|
|
||||||
/* We need to hide the dsa_8021q VLAN from the user.
|
/* On SJA1105 E/T, the switch doesn't implement the LOCKEDS
|
||||||
* Convert the TX VID into the pvid that is active in
|
* bit, so it doesn't tell us whether a FDB entry is static
|
||||||
* standalone and non-vlan_filtering modes, aka 1.
|
* or not.
|
||||||
* The RX VID is applied on the CPU port, which is not seen by
|
* But, of course, we can find out - we're the ones who added
|
||||||
* the bridge core anyway, so there's nothing to hide.
|
* it in the first place.
|
||||||
*/
|
*/
|
||||||
if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
|
if (priv->info->device_id == SJA1105E_DEVICE_ID ||
|
||||||
l2_lookup.vlanid = 1;
|
priv->info->device_id == SJA1105T_DEVICE_ID) {
|
||||||
cb(macaddr, l2_lookup.vlanid, false, data);
|
int match;
|
||||||
|
|
||||||
|
match = sja1105_find_static_fdb_entry(priv, port,
|
||||||
|
&l2_lookup);
|
||||||
|
l2_lookup.lockeds = (match >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to hide the dsa_8021q VLANs from the user. This
|
||||||
|
* basically means hiding the duplicates and only showing
|
||||||
|
* the pvid that is supposed to be active in standalone and
|
||||||
|
* non-vlan_filtering modes (aka 1).
|
||||||
|
* - For statically added FDB entries (bridge fdb add), we
|
||||||
|
* can convert the TX VID (coming from the CPU port) into the
|
||||||
|
* pvid and ignore the RX VIDs of the other ports.
|
||||||
|
* - For dynamically learned FDB entries, a single entry with
|
||||||
|
* no duplicates is learned - that which has the real port's
|
||||||
|
* pvid, aka RX VID.
|
||||||
|
*/
|
||||||
|
if (!dsa_port_is_vlan_filtering(&ds->ports[port])) {
|
||||||
|
if (l2_lookup.vlanid == tx_vid ||
|
||||||
|
l2_lookup.vlanid == rx_vid)
|
||||||
|
l2_lookup.vlanid = 1;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,6 @@ int sja1105_get_ts_info(struct dsa_switch *ds, int port,
|
||||||
info->phc_index = ptp_clock_index(priv->clock);
|
info->phc_index = ptp_clock_index(priv->clock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_get_ts_info);
|
|
||||||
|
|
||||||
int sja1105et_ptp_cmd(const void *ctx, const void *data)
|
int sja1105et_ptp_cmd(const void *ctx, const void *data)
|
||||||
{
|
{
|
||||||
|
@ -95,7 +94,6 @@ int sja1105et_ptp_cmd(const void *ctx, const void *data)
|
||||||
return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control,
|
return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control,
|
||||||
buf, SJA1105_SIZE_PTP_CMD);
|
buf, SJA1105_SIZE_PTP_CMD);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105et_ptp_cmd);
|
|
||||||
|
|
||||||
int sja1105pqrs_ptp_cmd(const void *ctx, const void *data)
|
int sja1105pqrs_ptp_cmd(const void *ctx, const void *data)
|
||||||
{
|
{
|
||||||
|
@ -113,7 +111,6 @@ int sja1105pqrs_ptp_cmd(const void *ctx, const void *data)
|
||||||
return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control,
|
return sja1105_spi_send_packed_buf(priv, SPI_WRITE, regs->ptp_control,
|
||||||
buf, SJA1105_SIZE_PTP_CMD);
|
buf, SJA1105_SIZE_PTP_CMD);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105pqrs_ptp_cmd);
|
|
||||||
|
|
||||||
/* The switch returns partial timestamps (24 bits for SJA1105 E/T, which wrap
|
/* The switch returns partial timestamps (24 bits for SJA1105 E/T, which wrap
|
||||||
* around in 0.135 seconds, and 32 bits for P/Q/R/S, wrapping around in 34.35
|
* around in 0.135 seconds, and 32 bits for P/Q/R/S, wrapping around in 34.35
|
||||||
|
@ -146,7 +143,6 @@ u64 sja1105_tstamp_reconstruct(struct sja1105_private *priv, u64 now,
|
||||||
|
|
||||||
return ts_reconstructed;
|
return ts_reconstructed;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_tstamp_reconstruct);
|
|
||||||
|
|
||||||
/* Reads the SPI interface for an egress timestamp generated by the switch
|
/* Reads the SPI interface for an egress timestamp generated by the switch
|
||||||
* for frames sent using management routes.
|
* for frames sent using management routes.
|
||||||
|
@ -219,7 +215,6 @@ int sja1105_ptpegr_ts_poll(struct sja1105_private *priv, int port, u64 *ts)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_ptpegr_ts_poll);
|
|
||||||
|
|
||||||
int sja1105_ptp_reset(struct sja1105_private *priv)
|
int sja1105_ptp_reset(struct sja1105_private *priv)
|
||||||
{
|
{
|
||||||
|
@ -240,7 +235,6 @@ int sja1105_ptp_reset(struct sja1105_private *priv)
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_ptp_reset);
|
|
||||||
|
|
||||||
static int sja1105_ptp_gettime(struct ptp_clock_info *ptp,
|
static int sja1105_ptp_gettime(struct ptp_clock_info *ptp,
|
||||||
struct timespec64 *ts)
|
struct timespec64 *ts)
|
||||||
|
@ -387,18 +381,13 @@ int sja1105_ptp_clock_register(struct sja1105_private *priv)
|
||||||
|
|
||||||
return sja1105_ptp_reset(priv);
|
return sja1105_ptp_reset(priv);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_ptp_clock_register);
|
|
||||||
|
|
||||||
void sja1105_ptp_clock_unregister(struct sja1105_private *priv)
|
void sja1105_ptp_clock_unregister(struct sja1105_private *priv)
|
||||||
{
|
{
|
||||||
if (IS_ERR_OR_NULL(priv->clock))
|
if (IS_ERR_OR_NULL(priv->clock))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&priv->refresh_work);
|
||||||
ptp_clock_unregister(priv->clock);
|
ptp_clock_unregister(priv->clock);
|
||||||
priv->clock = NULL;
|
priv->clock = NULL;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_ptp_clock_unregister);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Vladimir Oltean <olteanv@gmail.com>");
|
|
||||||
MODULE_DESCRIPTION("SJA1105 PHC Driver");
|
|
||||||
MODULE_LICENSE("GPL v2");
|
|
||||||
|
|
|
@ -100,7 +100,6 @@ int sja1105_spi_send_packed_buf(const struct sja1105_private *priv,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_spi_send_packed_buf);
|
|
||||||
|
|
||||||
/* If @rw is:
|
/* If @rw is:
|
||||||
* - SPI_WRITE: creates and sends an SPI write message at absolute
|
* - SPI_WRITE: creates and sends an SPI write message at absolute
|
||||||
|
@ -136,7 +135,6 @@ int sja1105_spi_send_int(const struct sja1105_private *priv,
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_spi_send_int);
|
|
||||||
|
|
||||||
/* Should be used if a @packed_buf larger than SJA1105_SIZE_SPI_MSG_MAXLEN
|
/* Should be used if a @packed_buf larger than SJA1105_SIZE_SPI_MSG_MAXLEN
|
||||||
* must be sent/received. Splitting the buffer into chunks and assembling
|
* must be sent/received. Splitting the buffer into chunks and assembling
|
||||||
|
|
|
@ -35,7 +35,6 @@ void sja1105_pack(void *buf, const u64 *val, int start, int end, size_t len)
|
||||||
}
|
}
|
||||||
dump_stack();
|
dump_stack();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_pack);
|
|
||||||
|
|
||||||
void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
|
void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
|
||||||
{
|
{
|
||||||
|
@ -53,7 +52,6 @@ void sja1105_unpack(const void *buf, u64 *val, int start, int end, size_t len)
|
||||||
start, end);
|
start, end);
|
||||||
dump_stack();
|
dump_stack();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_unpack);
|
|
||||||
|
|
||||||
void sja1105_packing(void *buf, u64 *val, int start, int end,
|
void sja1105_packing(void *buf, u64 *val, int start, int end,
|
||||||
size_t len, enum packing_op op)
|
size_t len, enum packing_op op)
|
||||||
|
@ -76,7 +74,6 @@ void sja1105_packing(void *buf, u64 *val, int start, int end,
|
||||||
}
|
}
|
||||||
dump_stack();
|
dump_stack();
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sja1105_packing);
|
|
||||||
|
|
||||||
/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
|
/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */
|
||||||
u32 sja1105_crc32(const void *buf, size_t len)
|
u32 sja1105_crc32(const void *buf, size_t len)
|
||||||
|
@ -233,11 +230,20 @@ sja1105pqrs_l2_lookup_params_entry_packing(void *buf, void *entry_ptr,
|
||||||
{
|
{
|
||||||
const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY;
|
const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY;
|
||||||
struct sja1105_l2_lookup_params_entry *entry = entry_ptr;
|
struct sja1105_l2_lookup_params_entry *entry = entry_ptr;
|
||||||
|
int offset, i;
|
||||||
|
|
||||||
|
for (i = 0, offset = 58; i < 5; i++, offset += 11)
|
||||||
|
sja1105_packing(buf, &entry->maxaddrp[i],
|
||||||
|
offset + 10, offset + 0, size, op);
|
||||||
sja1105_packing(buf, &entry->maxage, 57, 43, size, op);
|
sja1105_packing(buf, &entry->maxage, 57, 43, size, op);
|
||||||
|
sja1105_packing(buf, &entry->start_dynspc, 42, 33, size, op);
|
||||||
|
sja1105_packing(buf, &entry->drpnolearn, 32, 28, size, op);
|
||||||
sja1105_packing(buf, &entry->shared_learn, 27, 27, size, op);
|
sja1105_packing(buf, &entry->shared_learn, 27, 27, size, op);
|
||||||
sja1105_packing(buf, &entry->no_enf_hostprt, 26, 26, size, op);
|
sja1105_packing(buf, &entry->no_enf_hostprt, 26, 26, size, op);
|
||||||
sja1105_packing(buf, &entry->no_mgmt_learn, 25, 25, size, op);
|
sja1105_packing(buf, &entry->no_mgmt_learn, 25, 25, size, op);
|
||||||
|
sja1105_packing(buf, &entry->use_static, 24, 24, size, op);
|
||||||
|
sja1105_packing(buf, &entry->owr_dyn, 23, 23, size, op);
|
||||||
|
sja1105_packing(buf, &entry->learn_once, 22, 22, size, op);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ struct sja1105_l2_lookup_entry {
|
||||||
u64 mask_vlanid;
|
u64 mask_vlanid;
|
||||||
u64 mask_macaddr;
|
u64 mask_macaddr;
|
||||||
u64 iotag;
|
u64 iotag;
|
||||||
bool lockeds;
|
u64 lockeds;
|
||||||
union {
|
union {
|
||||||
/* LOCKEDS=1: Static FDB entries */
|
/* LOCKEDS=1: Static FDB entries */
|
||||||
struct {
|
struct {
|
||||||
|
@ -151,6 +151,7 @@ struct sja1105_l2_lookup_entry {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sja1105_l2_lookup_params_entry {
|
struct sja1105_l2_lookup_params_entry {
|
||||||
|
u64 maxaddrp[5]; /* P/Q/R/S only */
|
||||||
u64 start_dynspc; /* P/Q/R/S only */
|
u64 start_dynspc; /* P/Q/R/S only */
|
||||||
u64 drpnolearn; /* P/Q/R/S only */
|
u64 drpnolearn; /* P/Q/R/S only */
|
||||||
u64 use_static; /* P/Q/R/S only */
|
u64 use_static; /* P/Q/R/S only */
|
||||||
|
|
Loading…
Reference in New Issue