net: dsa: microchip: Fix reserved multicast address table programming

[ Upstream commit 96baf482ca ]

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: 457c182af5 ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Tristram Ha
2025-11-04 19:37:41 -08:00
committed by Greg Kroah-Hartman
parent 6f0295765f
commit 8803d2f90c
4 changed files with 91 additions and 16 deletions

View 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;

View 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
@@ -397,7 +397,6 @@
#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

View 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),
};

View 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,
};