]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: dsa: microchip: Fix reserved multicast address table programming
authorTristram Ha <tristram.ha@microchip.com>
Wed, 5 Nov 2025 03:37:41 +0000 (19:37 -0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Nov 2025 15:11:36 +0000 (07:11 -0800)
KSZ9477/KSZ9897 and LAN937X families of switches use a reserved multicast
address table for some specific forwarding with some multicast addresses,
like the one used in STP.  The hardware assumes the host port is the last
port in KSZ9897 family and port 5 in LAN937X family.  Most of the time
this assumption is correct but not in other cases like KSZ9477.
Originally the function just setups the first entry, but the others still
need update, especially for one common multicast address that is used by
PTP operation.

LAN937x also uses different register bits when accessing the reserved
table.

Fixes: 457c182af597 ("net: dsa: microchip: generic access to ksz9477 static and reserved table")
Signed-off-by: Tristram Ha <tristram.ha@microchip.com>
Tested-by: Ɓukasz Majewski <lukma@nabladev.com>
Link: https://patch.msgid.link/20251105033741.6455-1-Tristram.Ha@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/microchip/ksz9477_reg.h
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h

index d747ea1c41a79319ba736eea94552baa106296a7..5df8f153d511bced5dc00ba27ee2c5b56369dcae 100644 (file)
@@ -1355,9 +1355,15 @@ void ksz9477_config_cpu_port(struct dsa_switch *ds)
        }
 }
 
+#define RESV_MCAST_CNT 8
+
+static u8 reserved_mcast_map[RESV_MCAST_CNT] = { 0, 1, 3, 16, 32, 33, 2, 17 };
+
 int ksz9477_enable_stp_addr(struct ksz_device *dev)
 {
+       u8 i, ports, update;
        const u32 *masks;
+       bool override;
        u32 data;
        int ret;
 
@@ -1366,23 +1372,87 @@ int ksz9477_enable_stp_addr(struct ksz_device *dev)
        /* Enable Reserved multicast table */
        ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_RESV_MCAST_ENABLE, true);
 
-       /* Set the Override bit for forwarding BPDU packet to CPU */
-       ret = ksz_write32(dev, REG_SW_ALU_VAL_B,
-                         ALU_V_OVERRIDE | BIT(dev->cpu_port));
-       if (ret < 0)
-               return ret;
+       /* The reserved multicast address table has 8 entries.  Each entry has
+        * a default value of which port to forward.  It is assumed the host
+        * port is the last port in most of the switches, but that is not the
+        * case for KSZ9477 or maybe KSZ9897.  For LAN937X family the default
+        * port is port 5, the first RGMII port.  It is okay for LAN9370, a
+        * 5-port switch, but may not be correct for the other 8-port
+        * versions.  It is necessary to update the whole table to forward to
+        * the right ports.
+        * Furthermore PTP messages can use a reserved multicast address and
+        * the host will not receive them if this table is not correct.
+        */
+       for (i = 0; i < RESV_MCAST_CNT; i++) {
+               data = reserved_mcast_map[i] <<
+                       dev->info->shifts[ALU_STAT_INDEX];
+               data |= ALU_STAT_START |
+                       masks[ALU_STAT_DIRECT] |
+                       masks[ALU_RESV_MCAST_ADDR] |
+                       masks[ALU_STAT_READ];
+               ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+               if (ret < 0)
+                       return ret;
 
-       data = ALU_STAT_START | ALU_RESV_MCAST_ADDR | masks[ALU_STAT_WRITE];
+               /* wait to be finished */
+               ret = ksz9477_wait_alu_sta_ready(dev);
+               if (ret < 0)
+                       return ret;
 
-       ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
-       if (ret < 0)
-               return ret;
+               ret = ksz_read32(dev, REG_SW_ALU_VAL_B, &data);
+               if (ret < 0)
+                       return ret;
 
-       /* wait to be finished */
-       ret = ksz9477_wait_alu_sta_ready(dev);
-       if (ret < 0) {
-               dev_err(dev->dev, "Failed to update Reserved Multicast table\n");
-               return ret;
+               override = false;
+               ports = data & dev->port_mask;
+               switch (i) {
+               case 0:
+               case 6:
+                       /* Change the host port. */
+                       update = BIT(dev->cpu_port);
+                       override = true;
+                       break;
+               case 2:
+                       /* Change the host port. */
+                       update = BIT(dev->cpu_port);
+                       break;
+               case 4:
+               case 5:
+               case 7:
+                       /* Skip the host port. */
+                       update = dev->port_mask & ~BIT(dev->cpu_port);
+                       break;
+               default:
+                       update = ports;
+                       break;
+               }
+               if (update != ports || override) {
+                       data &= ~dev->port_mask;
+                       data |= update;
+                       /* Set Override bit to receive frame even when port is
+                        * closed.
+                        */
+                       if (override)
+                               data |= ALU_V_OVERRIDE;
+                       ret = ksz_write32(dev, REG_SW_ALU_VAL_B, data);
+                       if (ret < 0)
+                               return ret;
+
+                       data = reserved_mcast_map[i] <<
+                              dev->info->shifts[ALU_STAT_INDEX];
+                       data |= ALU_STAT_START |
+                               masks[ALU_STAT_DIRECT] |
+                               masks[ALU_RESV_MCAST_ADDR] |
+                               masks[ALU_STAT_WRITE];
+                       ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+                       if (ret < 0)
+                               return ret;
+
+                       /* wait to be finished */
+                       ret = ksz9477_wait_alu_sta_ready(dev);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        return 0;
index ff579920078ee3ca69899eb99576a66e26f31547..61ea11e3338e1b5195476f81160a78f125386333 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * Microchip KSZ9477 register definitions
  *
- * Copyright (C) 2017-2024 Microchip Technology Inc.
+ * Copyright (C) 2017-2025 Microchip Technology Inc.
  */
 
 #ifndef __KSZ9477_REGS_H
 
 #define ALU_RESV_MCAST_INDEX_M         (BIT(6) - 1)
 #define ALU_STAT_START                 BIT(7)
-#define ALU_RESV_MCAST_ADDR            BIT(1)
 
 #define REG_SW_ALU_VAL_A               0x0420
 
index a962055bfdbd8fbfc135b2dec73c222a213985c4..933ae8dc633783f7e23c3f302fd737b30a7a8fe2 100644 (file)
@@ -808,6 +808,8 @@ static const u16 ksz9477_regs[] = {
 static const u32 ksz9477_masks[] = {
        [ALU_STAT_WRITE]                = 0,
        [ALU_STAT_READ]                 = 1,
+       [ALU_STAT_DIRECT]               = 0,
+       [ALU_RESV_MCAST_ADDR]           = BIT(1),
        [P_MII_TX_FLOW_CTRL]            = BIT(5),
        [P_MII_RX_FLOW_CTRL]            = BIT(3),
 };
@@ -835,6 +837,8 @@ static const u8 ksz9477_xmii_ctrl1[] = {
 static const u32 lan937x_masks[] = {
        [ALU_STAT_WRITE]                = 1,
        [ALU_STAT_READ]                 = 2,
+       [ALU_STAT_DIRECT]               = BIT(3),
+       [ALU_RESV_MCAST_ADDR]           = BIT(2),
        [P_MII_TX_FLOW_CTRL]            = BIT(5),
        [P_MII_RX_FLOW_CTRL]            = BIT(3),
 };
index a1eb39771bb995676d98ce45e8105c158542f311..c65188cd3c0a0ed8dd75ee195cebbe47b3a01ada 100644 (file)
@@ -294,6 +294,8 @@ enum ksz_masks {
        DYNAMIC_MAC_TABLE_TIMESTAMP,
        ALU_STAT_WRITE,
        ALU_STAT_READ,
+       ALU_STAT_DIRECT,
+       ALU_RESV_MCAST_ADDR,
        P_MII_TX_FLOW_CTRL,
        P_MII_RX_FLOW_CTRL,
 };