|
|
@@ -109,6 +109,20 @@ static enum dsa_tag_protocol rtl83xx_get_tag_protocol(struct dsa_switch *ds,
|
|
|
return DSA_TAG_PROTO_TRAILER;
|
|
|
}
|
|
|
|
|
|
+static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
|
|
|
+ int port, int pvid)
|
|
|
+{
|
|
|
+ /* Set both inner and outer PVID of the port */
|
|
|
+ priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
|
|
|
+ priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
|
|
|
+ priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
|
|
|
+ PBVLAN_MODE_UNTAG_AND_PRITAG);
|
|
|
+ priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
|
|
|
+ PBVLAN_MODE_UNTAG_AND_PRITAG);
|
|
|
+
|
|
|
+ priv->ports[port].pvid = pvid;
|
|
|
+}
|
|
|
+
|
|
|
/* Initialize all VLANS */
|
|
|
static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
|
|
|
{
|
|
|
@@ -132,17 +146,22 @@ static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
|
|
|
info.l2_tunnel_list_id = -1;
|
|
|
}
|
|
|
|
|
|
- /* Initialize all vlans 0-4095 */
|
|
|
- for (int i = 0; i < MAX_VLANS; i ++)
|
|
|
+ /* Initialize normal VLANs 1-4095 */
|
|
|
+ for (int i = 1; i < MAX_VLANS; i ++)
|
|
|
priv->r->vlan_set_tagged(i, &info);
|
|
|
|
|
|
- /* reset PVIDs; defaults to 1 on reset */
|
|
|
+ /*
|
|
|
+ * Initialize the special VLAN 0 and reset PVIDs. The CPU port PVID
|
|
|
+ * is applied to packets from the CPU for untagged destinations,
|
|
|
+ * regardless if the actual ingress VID. Any port with untagged
|
|
|
+ * egress VLAN(s) must therefore be a member of VLAN 0 to support
|
|
|
+ * CPU port as ingress when VLAN filtering is enabled.
|
|
|
+ */
|
|
|
for (int i = 0; i <= priv->cpu_port; i++) {
|
|
|
- priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_INNER, 1);
|
|
|
- priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_OUTER, 1);
|
|
|
- priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_INNER, PBVLAN_MODE_UNTAG_AND_PRITAG);
|
|
|
- priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_OUTER, PBVLAN_MODE_UNTAG_AND_PRITAG);
|
|
|
+ rtl83xx_vlan_set_pvid(priv, i, 0);
|
|
|
+ info.tagged_ports |= BIT_ULL(i);
|
|
|
}
|
|
|
+ priv->r->vlan_set_tagged(0, &info);
|
|
|
|
|
|
/* Set forwarding action based on inner VLAN tag */
|
|
|
for (int i = 0; i < priv->cpu_port; i++)
|
|
|
@@ -1418,20 +1437,6 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
|
|
|
- int port, int pvid)
|
|
|
-{
|
|
|
- /* Set both inner and outer PVID of the port */
|
|
|
- priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
|
|
|
- priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
|
|
|
- priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
|
|
|
- PBVLAN_MODE_UNTAG_AND_PRITAG);
|
|
|
- priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
|
|
|
- PBVLAN_MODE_UNTAG_AND_PRITAG);
|
|
|
-
|
|
|
- priv->ports[port].pvid = pvid;
|
|
|
-}
|
|
|
-
|
|
|
static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
|
const struct switchdev_obj_port_vlan *vlan,
|
|
|
struct netlink_ext_ack *extack)
|
|
|
@@ -1443,7 +1448,8 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
|
pr_debug("%s port %d, vid %d, flags %x\n",
|
|
|
__func__, port, vlan->vid, vlan->flags);
|
|
|
|
|
|
- if(!vlan->vid) return 0;
|
|
|
+ /* Let no one mess with our special VLAN 0 */
|
|
|
+ if (!vlan->vid) return 0;
|
|
|
|
|
|
if (vlan->vid > 4095) {
|
|
|
dev_err(priv->dev, "VLAN out of range: %d", vlan->vid);
|
|
|
@@ -1456,10 +1462,20 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
|
|
|
|
|
|
mutex_lock(&priv->reg_mutex);
|
|
|
|
|
|
- if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
|
|
|
- rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
|
|
|
- else if (priv->ports[port].pvid == vlan->vid)
|
|
|
- rtl83xx_vlan_set_pvid(priv, port, 0);
|
|
|
+ /*
|
|
|
+ * Realtek switches copy frames as-is to/from the CPU. For a proper
|
|
|
+ * VLAN handling the 12 bit RVID field (= VLAN id) for incoming traffic
|
|
|
+ * and the 1 bit RVID_SEL field (0 = use inner tag, 1 = use outer tag)
|
|
|
+ * for outgoing traffic of the CPU tag structure need to be handled. As
|
|
|
+ * of now no such logic is in place. So for the CPU port keep the fixed
|
|
|
+ * PVID=0 from initial setup in place and ignore all subsequent settings.
|
|
|
+ */
|
|
|
+ if (port != priv->cpu_port) {
|
|
|
+ if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
|
|
|
+ rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
|
|
|
+ else if (priv->ports[port].pvid == vlan->vid)
|
|
|
+ rtl83xx_vlan_set_pvid(priv, port, 0);
|
|
|
+ }
|
|
|
|
|
|
/* Get port memberships of this vlan */
|
|
|
priv->r->vlan_tables_read(vlan->vid, &info);
|
|
|
@@ -1503,6 +1519,9 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
|
|
|
pr_debug("%s: port %d, vid %d, flags %x\n",
|
|
|
__func__, port, vlan->vid, vlan->flags);
|
|
|
|
|
|
+ /* Let no one mess with our special VLAN 0 */
|
|
|
+ if (!vlan->vid) return 0;
|
|
|
+
|
|
|
if (vlan->vid > 4095) {
|
|
|
dev_err(priv->dev, "VLAN out of range: %d", vlan->vid);
|
|
|
return -ENOTSUPP;
|