summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip.c224
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip.h7
2 files changed, 107 insertions, 124 deletions
diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.c b/drivers/net/dsa/lantiq/lantiq_gswip.c
index 2169c0814a48..25f6b46957a0 100644
--- a/drivers/net/dsa/lantiq/lantiq_gswip.c
+++ b/drivers/net/dsa/lantiq/lantiq_gswip.c
@@ -432,7 +432,7 @@ static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add)
vlan_active.index = port + 1;
vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
- vlan_active.key[0] = 0; /* vid */
+ vlan_active.key[0] = GSWIP_VLAN_UNAWARE_PVID;
vlan_active.val[0] = port + 1 /* fid */;
vlan_active.valid = add;
err = gswip_pce_table_entry_write(priv, &vlan_active);
@@ -446,7 +446,7 @@ static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add)
vlan_mapping.index = port + 1;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- vlan_mapping.val[0] = 0 /* vid */;
+ vlan_mapping.val[0] = GSWIP_VLAN_UNAWARE_PVID;
vlan_mapping.val[1] = BIT(port) | dsa_cpu_ports(priv->ds);
vlan_mapping.val[2] = 0;
err = gswip_pce_table_entry_write(priv, &vlan_mapping);
@@ -547,26 +547,63 @@ static int gswip_pce_load_microcode(struct gswip_priv *priv)
return 0;
}
+static void gswip_port_commit_pvid(struct gswip_priv *priv, int port)
+{
+ struct dsa_port *dp = dsa_to_port(priv->ds, port);
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
+ u32 vinr;
+ int idx;
+
+ if (!dsa_port_is_user(dp))
+ return;
+
+ if (br) {
+ u16 pvid = GSWIP_VLAN_UNAWARE_PVID;
+
+ if (br_vlan_enabled(br))
+ br_vlan_get_pvid(br, &pvid);
+
+ /* VLAN-aware bridge ports with no PVID will use Active VLAN
+ * index 0. The expectation is that this drops all untagged and
+ * VID-0 tagged ingress traffic.
+ */
+ idx = 0;
+ for (int i = priv->hw_info->max_ports;
+ i < ARRAY_SIZE(priv->vlans); i++) {
+ if (priv->vlans[i].bridge == br &&
+ priv->vlans[i].vid == pvid) {
+ idx = i;
+ break;
+ }
+ }
+ } else {
+ /* The Active VLAN table index as configured by
+ * gswip_add_single_port_br()
+ */
+ idx = port + 1;
+ }
+
+ vinr = idx ? GSWIP_PCE_VCTRL_VINR_ALL : GSWIP_PCE_VCTRL_VINR_TAGGED;
+ gswip_switch_mask(priv, GSWIP_PCE_VCTRL_VINR,
+ FIELD_PREP(GSWIP_PCE_VCTRL_VINR, vinr),
+ GSWIP_PCE_VCTRL(port));
+
+ /* GSWIP 2.2 (GRX300) and later program here the VID directly. */
+ gswip_switch_w(priv, idx, GSWIP_PCE_DEFPVID(port));
+}
+
static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering,
struct netlink_ext_ack *extack)
{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
- /* Do not allow changing the VLAN filtering options while in bridge */
- if (bridge && !!(priv->port_vlan_filter & BIT(port)) != vlan_filtering) {
- NL_SET_ERR_MSG_MOD(extack,
- "Dynamic toggling of vlan_filtering not supported");
- return -EIO;
- }
-
if (vlan_filtering) {
/* Use tag based VLAN */
gswip_switch_mask(priv,
GSWIP_PCE_VCTRL_VSR,
GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |
- GSWIP_PCE_VCTRL_VEMR,
+ GSWIP_PCE_VCTRL_VEMR | GSWIP_PCE_VCTRL_VID0,
GSWIP_PCE_VCTRL(port));
gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_TVM, 0,
GSWIP_PCE_PCTRL_0p(port));
@@ -574,13 +611,15 @@ static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
/* Use port based VLAN */
gswip_switch_mask(priv,
GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |
- GSWIP_PCE_VCTRL_VEMR,
+ GSWIP_PCE_VCTRL_VEMR | GSWIP_PCE_VCTRL_VID0,
GSWIP_PCE_VCTRL_VSR,
GSWIP_PCE_VCTRL(port));
gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_TVM,
GSWIP_PCE_PCTRL_0p(port));
}
+ gswip_port_commit_pvid(priv, port);
+
return 0;
}
@@ -677,8 +716,6 @@ static int gswip_setup(struct dsa_switch *ds)
ds->mtu_enforcement_ingress = true;
- ds->configure_vlan_while_not_filtering = false;
-
return 0;
}
@@ -750,85 +787,25 @@ static int gswip_vlan_active_remove(struct gswip_priv *priv, int idx)
return err;
}
-static int gswip_vlan_add_unaware(struct gswip_priv *priv,
- struct net_device *bridge, int port)
-{
- struct gswip_pce_table_entry vlan_mapping = {0,};
- unsigned int max_ports = priv->hw_info->max_ports;
- bool active_vlan_created = false;
- int idx = -1;
- int i;
- int err;
-
- /* Check if there is already a page for this bridge */
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (priv->vlans[i].bridge == bridge) {
- idx = i;
- break;
- }
- }
-
- /* If this bridge is not programmed yet, add a Active VLAN table
- * entry in a free slot and prepare the VLAN mapping table entry.
- */
- if (idx == -1) {
- idx = gswip_vlan_active_create(priv, bridge, -1, 0);
- if (idx < 0)
- return idx;
- active_vlan_created = true;
-
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- /* VLAN ID byte, maps to the VLAN ID of vlan active table */
- vlan_mapping.val[0] = 0;
- } else {
- /* Read the existing VLAN mapping entry from the switch */
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- err = gswip_pce_table_entry_read(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to read VLAN mapping: %d\n",
- err);
- return err;
- }
- }
-
- /* Update the VLAN mapping entry and write it to the switch */
- vlan_mapping.val[1] |= dsa_cpu_ports(priv->ds);
- vlan_mapping.val[1] |= BIT(port);
- err = gswip_pce_table_entry_write(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
- /* In case an Active VLAN was creaetd delete it again */
- if (active_vlan_created)
- gswip_vlan_active_remove(priv, idx);
- return err;
- }
-
- gswip_switch_w(priv, 0, GSWIP_PCE_DEFPVID(port));
- return 0;
-}
-
-static int gswip_vlan_add_aware(struct gswip_priv *priv,
- struct net_device *bridge, int port,
- u16 vid, bool untagged,
- bool pvid)
+static int gswip_vlan_add(struct gswip_priv *priv, struct net_device *bridge,
+ int port, u16 vid, bool untagged, bool pvid,
+ bool vlan_aware)
{
struct gswip_pce_table_entry vlan_mapping = {0,};
unsigned int max_ports = priv->hw_info->max_ports;
unsigned int cpu_ports = dsa_cpu_ports(priv->ds);
bool active_vlan_created = false;
- int idx = -1;
- int fid = -1;
- int i;
- int err;
+ int fid = -1, idx = -1;
+ int i, err;
/* Check if there is already a page for this bridge */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge) {
- if (fid != -1 && fid != priv->vlans[i].fid)
- dev_err(priv->dev, "one bridge with multiple flow ids\n");
- fid = priv->vlans[i].fid;
+ if (vlan_aware) {
+ if (fid != -1 && fid != priv->vlans[i].fid)
+ dev_err(priv->dev, "one bridge with multiple flow ids\n");
+ fid = priv->vlans[i].fid;
+ }
if (priv->vlans[i].vid == vid) {
idx = i;
break;
@@ -847,8 +824,6 @@ static int gswip_vlan_add_aware(struct gswip_priv *priv,
vlan_mapping.index = idx;
vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- /* VLAN ID byte, maps to the VLAN ID of vlan active table */
- vlan_mapping.val[0] = vid;
} else {
/* Read the existing VLAN mapping entry from the switch */
vlan_mapping.index = idx;
@@ -861,11 +836,13 @@ static int gswip_vlan_add_aware(struct gswip_priv *priv,
}
}
+ /* VLAN ID byte, maps to the VLAN ID of vlan active table */
vlan_mapping.val[0] = vid;
/* Update the VLAN mapping entry and write it to the switch */
vlan_mapping.val[1] |= cpu_ports;
- vlan_mapping.val[2] |= cpu_ports;
vlan_mapping.val[1] |= BIT(port);
+ if (vlan_aware)
+ vlan_mapping.val[2] |= cpu_ports;
if (untagged)
vlan_mapping.val[2] &= ~BIT(port);
else
@@ -879,15 +856,14 @@ static int gswip_vlan_add_aware(struct gswip_priv *priv,
return err;
}
- if (pvid)
- gswip_switch_w(priv, idx, GSWIP_PCE_DEFPVID(port));
+ gswip_port_commit_pvid(priv, port);
return 0;
}
static int gswip_vlan_remove(struct gswip_priv *priv,
struct net_device *bridge, int port,
- u16 vid, bool pvid, bool vlan_aware)
+ u16 vid)
{
struct gswip_pce_table_entry vlan_mapping = {0,};
unsigned int max_ports = priv->hw_info->max_ports;
@@ -898,14 +874,15 @@ static int gswip_vlan_remove(struct gswip_priv *priv,
/* Check if there is already a page for this bridge */
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge &&
- (!vlan_aware || priv->vlans[i].vid == vid)) {
+ priv->vlans[i].vid == vid) {
idx = i;
break;
}
}
if (idx == -1) {
- dev_err(priv->dev, "bridge to leave does not exists\n");
+ dev_err(priv->dev, "Port %d cannot find VID %u of bridge %s\n",
+ port, vid, bridge ? bridge->name : "(null)");
return -ENOENT;
}
@@ -935,9 +912,7 @@ static int gswip_vlan_remove(struct gswip_priv *priv,
}
}
- /* GSWIP 2.2 (GRX300) and later program here the VID directly. */
- if (pvid)
- gswip_switch_w(priv, 0, GSWIP_PCE_DEFPVID(port));
+ gswip_port_commit_pvid(priv, port);
return 0;
}
@@ -951,17 +926,15 @@ static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
struct gswip_priv *priv = ds->priv;
int err;
- /* When the bridge uses VLAN filtering we have to configure VLAN
- * specific bridges. No bridge is configured here.
+ /* Set up the VLAN for VLAN-unaware bridging for this port, and remove
+ * it from the "single-port bridge" through which it was operating as
+ * standalone.
*/
- if (!br_vlan_enabled(br)) {
- err = gswip_vlan_add_unaware(priv, br, port);
- if (err)
- return err;
- priv->port_vlan_filter &= ~BIT(port);
- } else {
- priv->port_vlan_filter |= BIT(port);
- }
+ err = gswip_vlan_add(priv, br, port, GSWIP_VLAN_UNAWARE_PVID,
+ true, true, false);
+ if (err)
+ return err;
+
return gswip_add_single_port_br(priv, port, false);
}
@@ -971,13 +944,11 @@ static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,
struct net_device *br = bridge.dev;
struct gswip_priv *priv = ds->priv;
- gswip_add_single_port_br(priv, port, true);
-
- /* When the bridge uses VLAN filtering we have to configure VLAN
- * specific bridges. No bridge is configured here.
+ /* Add the port back to the "single-port bridge", and remove it from
+ * the VLAN-unaware PVID created for this bridge.
*/
- if (!br_vlan_enabled(br))
- gswip_vlan_remove(priv, br, port, 0, true, false);
+ gswip_add_single_port_br(priv, port, true);
+ gswip_vlan_remove(priv, br, port, GSWIP_VLAN_UNAWARE_PVID);
}
static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,
@@ -1036,6 +1007,9 @@ static int gswip_port_vlan_add(struct dsa_switch *ds, int port,
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
int err;
+ if (vlan->vid == GSWIP_VLAN_UNAWARE_PVID)
+ return 0;
+
err = gswip_port_vlan_prepare(ds, port, vlan, extack);
if (err)
return err;
@@ -1048,8 +1022,8 @@ static int gswip_port_vlan_add(struct dsa_switch *ds, int port,
if (dsa_is_cpu_port(ds, port))
return 0;
- return gswip_vlan_add_aware(priv, bridge, port, vlan->vid,
- untagged, pvid);
+ return gswip_vlan_add(priv, bridge, port, vlan->vid, untagged, pvid,
+ true);
}
static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
@@ -1057,7 +1031,9 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
{
struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+
+ if (vlan->vid == GSWIP_VLAN_UNAWARE_PVID)
+ return 0;
/* We have to receive all packets on the CPU port and should not
* do any VLAN filtering here. This is also called with bridge
@@ -1067,7 +1043,7 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
if (dsa_is_cpu_port(ds, port))
return 0;
- return gswip_vlan_remove(priv, bridge, port, vlan->vid, pvid, true);
+ return gswip_vlan_remove(priv, bridge, port, vlan->vid);
}
static void gswip_port_fast_age(struct dsa_switch *ds, int port)
@@ -1140,9 +1116,9 @@ static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
}
static int gswip_port_fdb(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid, bool add)
+ struct net_device *bridge, const unsigned char *addr,
+ u16 vid, bool add)
{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
struct gswip_priv *priv = ds->priv;
struct gswip_pce_table_entry mac_bridge = {0,};
unsigned int max_ports = priv->hw_info->max_ports;
@@ -1150,10 +1126,6 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port,
int i;
int err;
- /* Operation not supported on the CPU port, don't throw errors */
- if (!bridge)
- return 0;
-
for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
if (priv->vlans[i].bridge == bridge) {
fid = priv->vlans[i].fid;
@@ -1188,14 +1160,20 @@ static int gswip_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
- return gswip_port_fdb(ds, port, addr, vid, true);
+ if (db.type != DSA_DB_BRIDGE)
+ return -EOPNOTSUPP;
+
+ return gswip_port_fdb(ds, port, db.bridge.dev, addr, vid, true);
}
static int gswip_port_fdb_del(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
- return gswip_port_fdb(ds, port, addr, vid, false);
+ if (db.type != DSA_DB_BRIDGE)
+ return -EOPNOTSUPP;
+
+ return gswip_port_fdb(ds, port, db.bridge.dev, addr, vid, false);
}
static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,
diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.h b/drivers/net/dsa/lantiq/lantiq_gswip.h
index 2df9c8e8cfd0..69c8d2deff2d 100644
--- a/drivers/net/dsa/lantiq/lantiq_gswip.h
+++ b/drivers/net/dsa/lantiq/lantiq_gswip.h
@@ -159,6 +159,10 @@
#define GSWIP_PCE_PCTRL_0_PSTATE_MASK GENMASK(2, 0)
#define GSWIP_PCE_VCTRL(p) (0x485 + ((p) * 0xA))
#define GSWIP_PCE_VCTRL_UVR BIT(0) /* Unknown VLAN Rule */
+#define GSWIP_PCE_VCTRL_VINR GENMASK(2, 1) /* VLAN Ingress Tag Rule */
+#define GSWIP_PCE_VCTRL_VINR_ALL 0 /* Admit tagged and untagged packets */
+#define GSWIP_PCE_VCTRL_VINR_TAGGED 1 /* Admit only tagged packets */
+#define GSWIP_PCE_VCTRL_VINR_UNTAGGED 2 /* Admit only untagged packets */
#define GSWIP_PCE_VCTRL_VIMR BIT(3) /* VLAN Ingress Member violation rule */
#define GSWIP_PCE_VCTRL_VEMR BIT(4) /* VLAN Egress Member violation rule */
#define GSWIP_PCE_VCTRL_VSR BIT(5) /* VLAN Security */
@@ -222,6 +226,8 @@
*/
#define GSWIP_MAX_PACKET_LENGTH 2400
+#define GSWIP_VLAN_UNAWARE_PVID 0
+
struct gswip_pce_microcode {
u16 val_3;
u16 val_2;
@@ -268,7 +274,6 @@ struct gswip_priv {
struct gswip_vlan vlans[64];
int num_gphy_fw;
struct gswip_gphy_fw *gphy_fw;
- u32 port_vlan_filter;
struct mutex pce_table_lock;
u16 version;
};