summaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa/sja1105/sja1105_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/sja1105/sja1105_main.c')
-rw-r--r--drivers/net/dsa/sja1105/sja1105_main.c143
1 files changed, 60 insertions, 83 deletions
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 32bf3a7cc3b6..d073baffc20b 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -218,7 +218,7 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv)
/* This selects between Independent VLAN Learning (IVL) and
* Shared VLAN Learning (SVL)
*/
- .shared_learn = false,
+ .shared_learn = true,
/* Don't discard management traffic based on ENFPORT -
* we don't perform SMAC port enforcement anyway, so
* what we are setting here doesn't matter.
@@ -625,6 +625,7 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv,
if (of_property_read_u32(child, "reg", &index) < 0) {
dev_err(dev, "Port number not defined in device tree "
"(property \"reg\")\n");
+ of_node_put(child);
return -ENODEV;
}
@@ -634,6 +635,7 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv,
dev_err(dev, "Failed to read phy-mode or "
"phy-interface-type property for port %d\n",
index);
+ of_node_put(child);
return -ENODEV;
}
ports[index].phy_mode = phy_mode;
@@ -643,6 +645,7 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv,
if (!of_phy_is_fixed_link(child)) {
dev_err(dev, "phy-handle or fixed-link "
"properties missing!\n");
+ of_node_put(child);
return -ENODEV;
}
/* phy-handle is missing, but fixed-link isn't.
@@ -1089,8 +1092,13 @@ int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port,
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);
+ if (dsa_port_is_vlan_filtering(&ds->ports[port])) {
+ l2_lookup.mask_vlanid = VLAN_VID_MASK;
+ l2_lookup.mask_iotag = BIT(0);
+ } else {
+ l2_lookup.mask_vlanid = 0;
+ l2_lookup.mask_iotag = 0;
+ }
l2_lookup.destports = BIT(port);
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
@@ -1147,8 +1155,13 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port,
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);
+ if (dsa_port_is_vlan_filtering(&ds->ports[port])) {
+ l2_lookup.mask_vlanid = VLAN_VID_MASK;
+ l2_lookup.mask_iotag = BIT(0);
+ } else {
+ l2_lookup.mask_vlanid = 0;
+ l2_lookup.mask_iotag = 0;
+ }
l2_lookup.destports = BIT(port);
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
@@ -1178,60 +1191,31 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid)
{
struct sja1105_private *priv = ds->priv;
- 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
- * 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.
+ /* dsa_8021q is in effect when the bridge's vlan_filtering isn't,
+ * so the switch still does some VLAN processing internally.
+ * But Shared VLAN Learning (SVL) is also active, and it will take
+ * care of autonomous forwarding between the unique pvid's of each
+ * port. Here we just make sure that users can't add duplicate FDB
+ * entries when in this mode - the actual VID doesn't matter except
+ * for what gets printed in 'bridge fdb show'. In the case of zero,
+ * no VID gets printed at all.
*/
- for (i = 0; i < SJA1105_NUM_PORTS; i++) {
- if (i == port)
- continue;
- if (i == dsa_upstream_port(priv->ds, port))
- continue;
+ if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
+ vid = 0;
- rx_vid = dsa_8021q_rx_vid(ds, i);
- rc = priv->info->fdb_add_cmd(ds, port, addr, rx_vid);
- if (rc < 0)
- return rc;
- }
- tx_vid = dsa_8021q_tx_vid(ds, port);
- return priv->info->fdb_add_cmd(ds, port, addr, tx_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;
- u16 rx_vid, tx_vid;
- int rc, i;
-
- if (dsa_port_is_vlan_filtering(&ds->ports[port]))
- return priv->info->fdb_del_cmd(ds, port, addr, vid);
- for (i = 0; i < SJA1105_NUM_PORTS; i++) {
- if (i == port)
- continue;
- if (i == dsa_upstream_port(priv->ds, port))
- continue;
+ if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
+ vid = 0;
- rx_vid = dsa_8021q_rx_vid(ds, i);
- rc = priv->info->fdb_del_cmd(ds, port, addr, rx_vid);
- if (rc < 0)
- return rc;
- }
- tx_vid = dsa_8021q_tx_vid(ds, port);
- return priv->info->fdb_del_cmd(ds, port, addr, tx_vid);
+ return priv->info->fdb_del_cmd(ds, port, addr, vid);
}
static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
@@ -1270,39 +1254,9 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
continue;
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
- /* On SJA1105 E/T, the switch doesn't implement the LOCKEDS
- * bit, so it doesn't tell us whether a FDB entry is static
- * or not.
- * But, of course, we can find out - we're the ones who added
- * it in the first place.
- */
- 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;
- else
- continue;
- }
+ /* We need to hide the dsa_8021q VLANs from the user. */
+ if (!dsa_port_is_vlan_filtering(&ds->ports[port]))
+ l2_lookup.vlanid = 0;
cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data);
}
return 0;
@@ -1594,6 +1548,7 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
*/
static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
{
+ struct sja1105_l2_lookup_params_entry *l2_lookup_params;
struct sja1105_general_params_entry *general_params;
struct sja1105_private *priv = ds->priv;
struct sja1105_table *table;
@@ -1622,6 +1577,28 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
general_params->incl_srcpt1 = enabled;
general_params->incl_srcpt0 = enabled;
+ /* VLAN filtering => independent VLAN learning.
+ * No VLAN filtering => shared VLAN learning.
+ *
+ * In shared VLAN learning mode, untagged traffic still gets
+ * pvid-tagged, and the FDB table gets populated with entries
+ * containing the "real" (pvid or from VLAN tag) VLAN ID.
+ * However the switch performs a masked L2 lookup in the FDB,
+ * effectively only looking up a frame's DMAC (and not VID) for the
+ * forwarding decision.
+ *
+ * This is extremely convenient for us, because in modes with
+ * vlan_filtering=0, dsa_8021q actually installs unique pvid's into
+ * each front panel port. This is good for identification but breaks
+ * learning badly - the VID of the learnt FDB entry is unique, aka
+ * no frames coming from any other port are going to have it. So
+ * for forwarding purposes, this is as though learning was broken
+ * (all frames get flooded).
+ */
+ table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS];
+ l2_lookup_params = table->entries;
+ l2_lookup_params->shared_learn = !enabled;
+
rc = sja1105_static_config_reload(priv);
if (rc)
dev_err(ds->dev, "Failed to change VLAN Ethertype\n");
@@ -1751,6 +1728,8 @@ static void sja1105_teardown(struct dsa_switch *ds)
cancel_work_sync(&priv->tagger_data.rxtstamp_work);
skb_queue_purge(&priv->tagger_data.skb_rxtstamp_queue);
+ sja1105_ptp_clock_unregister(priv);
+ sja1105_static_config_free(&priv->static_config);
}
static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,
@@ -2208,9 +2187,7 @@ static int sja1105_remove(struct spi_device *spi)
{
struct sja1105_private *priv = spi_get_drvdata(spi);
- sja1105_ptp_clock_unregister(priv);
dsa_unregister_switch(priv->ds);
- sja1105_static_config_free(&priv->static_config);
return 0;
}