From: Tristram Ha <tristram...@microchip.com> Fix VLAN filtering operation in kernel 4.15 and later.
Fixes: b987e98e50ab90e5 ("dsa: add DSA switch driver for Microchip KSZ9477") Signed-off-by: Tristram Ha <tristram...@microchip.com> --- drivers/net/dsa/microchip/ksz9477.c | 44 ++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 57a146a..28e3fa5 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -496,19 +496,33 @@ static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag) { struct ksz_device *dev = ds->priv; + u32 vlan_table[3]; if (flag) { + vlan_table[0] = VLAN_VALID | 0; + vlan_table[1] = 0; + vlan_table[2] = dev->port_mask; + if (ksz9477_set_vlan_table(dev, 0, vlan_table)) { + dev_dbg(dev->dev, "Failed to set vlan table\n"); + return 0; + } ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, - PORT_VLAN_LOOKUP_VID_0, true); - ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, - true); + (PORT_VLAN_LOOKUP_VID_0 | PORT_INGRESS_FILTER), + true); ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, true); } else { + /* VLAN 1 entry is required to run properly. */ + vlan_table[0] = VLAN_VALID | 0; + vlan_table[1] = 0; + vlan_table[2] = dev->port_mask; + if (ksz9477_set_vlan_table(dev, 1, vlan_table)) { + dev_dbg(dev->dev, "Failed to set vlan table\n"); + return 0; + } ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_VLAN_ENABLE, false); - ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, - false); ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, - PORT_VLAN_LOOKUP_VID_0, false); + (PORT_VLAN_LOOKUP_VID_0 | PORT_INGRESS_FILTER), + false); } return 0; @@ -521,6 +535,7 @@ static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port, u32 vlan_table[3]; u16 vid; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { if (ksz9477_get_vlan_table(dev, vid, vlan_table)) { @@ -533,7 +548,12 @@ static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port, vlan_table[1] |= BIT(port); else vlan_table[1] &= ~BIT(port); - vlan_table[1] &= ~(BIT(dev->cpu_port)); + + /* Keep host port untagged when setting pvid. */ + if (untagged && pvid) + vlan_table[1] |= BIT(dev->cpu_port); + else + vlan_table[1] &= ~(BIT(dev->cpu_port)); vlan_table[2] |= BIT(port) | BIT(dev->cpu_port); @@ -543,7 +563,7 @@ static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port, } /* change PVID */ - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) + if (pvid) ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid); } } @@ -568,6 +588,10 @@ static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, vlan_table[2] &= ~BIT(port); + /* Remove VLAN entry if no longer used. */ + if (vid > 1 && !(vlan_table[2] & ~dev->host_mask)) + vlan_table[0] &= ~VLAN_VALID; + if (pvid == vid) pvid = 1; @@ -1130,6 +1154,10 @@ static int ksz9477_setup(struct dsa_switch *ds) return ret; } + /* Required for port partitioning. */ + ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, + true); + /* accept packet up to 2000bytes */ ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true); -- 1.9.1