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:
David S. Miller 2019-06-27 11:03:22 -07:00
commit 5b1bf3f644
7 changed files with 343 additions and 70 deletions

View File

@ -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

View File

@ -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,

View File

@ -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 ||
priv->info->device_id == SJA1105T_DEVICE_ID) {
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; l2_lookup.vlanid = 1;
cb(macaddr, l2_lookup.vlanid, false, data); else
continue;
}
cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data);
} }
return 0; return 0;
} }

View File

@ -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");

View File

@ -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

View File

@ -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;
} }

View File

@ -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 */