summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-09-03 10:31:36 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-09-03 10:31:36 +0200
commit1ab75e8bbfbff3e93e0ea06f33bd970c5324d31c (patch)
tree1c10747113f34f356f1af9dc7b474c1f05a005fb
parente16acadb9a0f7a0f04318e4b3a8e89e8ddd0c905 (diff)
parent6815415d68b98bd34d22aa0631c71b0fb7cbc7f5 (diff)
downloadlinux-next-1ab75e8bbfbff3e93e0ea06f33bd970c5324d31c.tar.gz
linux-next-1ab75e8bbfbff3e93e0ea06f33bd970c5324d31c.zip
Merge branch 'net-stmmac-dwmac4-auto-discover-uc-filter-size'
Maxime Chevallier says: ==================== net: stmmac: dwmac4: Auto-discover UC filter size Following my attempts to get clean ethtool selftests runs on stmmac [1], several issues were found with the unicast filtering. [1]: https://lore.kernel.org/netdev/20260827134004.45ffb57e@kernel.org/ This lead to the discovery that on dwmac4 platforms, the unicast filter size is always falling back to the default value of 1 entry. On older variants, the size comes from devicetree. With a single-entry filter, we directly fallback to unicast promisc as soon as any entry is added in the dev->uc list. dwmac4 exposes through the HW_Features0 register the actual size of the unicast filter, which is made of 3 banks (details in patch 2). Let's use this feature to grab the filter size. Results on actual hardware : - imx8mp : goes from 1 entry to 64 - stm32mp1 : goes from 1 entry to 4 - jh7110 : goes from 1 entry to 9 - yt6801 : still 1 single entry, on extra implemented in HW ==================== Link: https://patch.msgid.link/20260831070121.349778-1-maxime.chevallier@bootlin.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c60
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c39
5 files changed, 99 insertions, 13 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 927ea6230073..1729fb29ece3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -419,6 +419,8 @@ struct dma_features {
unsigned int half_duplex;
unsigned int hash_filter;
unsigned int multi_addr;
+ unsigned int additional_32_addr;
+ unsigned int additional_64_addr;
unsigned int pcs;
unsigned int sma_mdio;
unsigned int pmt_remote_wake_up;
@@ -627,6 +629,7 @@ struct mac_device_info {
void __iomem *pcsr; /* vpointer to device CSRs */
unsigned int multicast_filter_bins;
unsigned int unicast_filter_entries;
+ unsigned int multi_addr;
unsigned int mcast_bits_log2;
unsigned int rx_csum;
unsigned int num_vlan;
@@ -635,6 +638,8 @@ struct mac_device_info {
u8 vlan_fail_q;
bool hw_vlan_en;
bool reverse_sgmii_enable;
+ bool additional_32_addr;
+ bool additional_64_addr;
/* This spinlock protects read-modify-write of the interrupt
* mask/enable registers.
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index 6382836828ba..696846531229 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -75,8 +75,6 @@
#define GMAC_PACKET_FILTER_IPFE BIT(20)
#define GMAC_PACKET_FILTER_RA BIT(31)
-#define GMAC_MAX_PERFECT_ADDRESSES 128
-
/* MAC RX Queue Enable */
#define GMAC_RX_QUEUE_CLEAR(queue) ~(GENMASK(1, 0) << ((queue) * 2))
#define GMAC_RX_AV_QUEUE_ENABLE(queue) BIT((queue) * 2)
@@ -176,7 +174,9 @@ enum power_event {
/* MAC HW features0 bitmap */
#define GMAC_HW_FEAT_SAVLANINS BIT(27)
-#define GMAC_HW_FEAT_ADDMAC BIT(18)
+#define GMAC_HW_FEAT_MACADR64SEL BIT(24)
+#define GMAC_HW_FEAT_MACADR32SEL BIT(23)
+#define GMAC_HW_FEAT_ADDMAC GENMASK(22, 18)
#define GMAC_HW_FEAT_RXCOESEL BIT(16)
#define GMAC_HW_FEAT_TXCOSEL BIT(14)
#define GMAC_HW_FEAT_EEESEL BIT(13)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 70c8b5a373d3..cd6c5d6f42b5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -365,22 +365,63 @@ static void dwmac4_pmt(struct mac_device_info *hw, unsigned long mode)
writel(pmt, ioaddr + GMAC_PMT);
}
+/**
+ * dwmac4_umac_addr_slot - Get the UC filter slot for the Nth MAC address
+ * @hw: The MAC device info
+ * @reg_n: The MAC address's index in the UC list
+ *
+ * On dwmac4 the slots available for Unicast MAC filtering are configured when
+ * integrating the IP.
+ *
+ * - Slot 0 is always available, used to store the primary MAC address
+ * - From 1 to 31, the number of implemented slots is set in hw->multi_addr
+ * - From 32 to 63, the range is available if hw->additional_32_addr
+ * is set
+ * - From 64 to 127, the range is available if hw->additional_64_addr is set
+ *
+ * All the ranges can be configured independently (e.g. 64 -> 127 can be
+ * available, but not 32 -> 63)
+ *
+ * Returns: The physical slot index in the UC filter
+ */
+static unsigned int dwmac4_umac_addr_slot(struct mac_device_info *hw,
+ unsigned int reg_n)
+{
+ unsigned int empty_slots = 0;
+
+ /* reg_n is in the 1-31 bank : 1 to 1 mapping */
+ if (reg_n < (hw->multi_addr + 1))
+ return reg_n;
+
+ /* Gap between the last address in the 1->31 range and the next slot */
+ if (hw->additional_32_addr)
+ empty_slots = 32 - (hw->multi_addr + 1);
+ else if (hw->additional_64_addr)
+ empty_slots = 64 - (hw->multi_addr + 1);
+
+ return reg_n + empty_slots;
+}
+
static void dwmac4_set_umac_addr(struct mac_device_info *hw,
const unsigned char *addr, unsigned int reg_n)
{
void __iomem *ioaddr = hw->pcsr;
+ unsigned int slot;
- stmmac_dwmac4_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
- GMAC_ADDR_LOW(reg_n));
+ slot = dwmac4_umac_addr_slot(hw, reg_n);
+ stmmac_dwmac4_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(slot),
+ GMAC_ADDR_LOW(slot));
}
static void dwmac4_get_umac_addr(struct mac_device_info *hw,
unsigned char *addr, unsigned int reg_n)
{
void __iomem *ioaddr = hw->pcsr;
+ unsigned int slot;
- stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
- GMAC_ADDR_LOW(reg_n));
+ slot = dwmac4_umac_addr_slot(hw, reg_n);
+ stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(slot),
+ GMAC_ADDR_LOW(slot));
}
static int dwmac4_set_lpi_mode(struct mac_device_info *hw,
@@ -522,9 +563,6 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
/* Handle multiple unicast addresses */
if (netdev_uc_count(dev) + 1 > hw->unicast_filter_entries) {
- /* Switch to promiscuous mode if more than 128 addrs
- * are required
- */
value |= GMAC_PACKET_FILTER_PR;
} else {
struct netdev_hw_addr *ha;
@@ -535,9 +573,11 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
reg++;
}
- while (reg < GMAC_MAX_PERFECT_ADDRESSES) {
- writel(0, ioaddr + GMAC_ADDR_HIGH(reg));
- writel(0, ioaddr + GMAC_ADDR_LOW(reg));
+ while (reg < hw->unicast_filter_entries) {
+ unsigned int slot = dwmac4_umac_addr_slot(hw, reg);
+
+ writel(0, ioaddr + GMAC_ADDR_HIGH(slot));
+ writel(0, ioaddr + GMAC_ADDR_LOW(slot));
reg++;
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 23ffe1adcd0d..14ac3f0e51f7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -388,6 +388,8 @@ static int dwmac4_get_hw_feature(void __iomem *ioaddr,
dma_cap->half_duplex = (hw_cap & GMAC_HW_FEAT_HDSEL) >> 2;
dma_cap->vlhash = (hw_cap & GMAC_HW_FEAT_VLHASH) >> 4;
dma_cap->multi_addr = (hw_cap & GMAC_HW_FEAT_ADDMAC) >> 18;
+ dma_cap->additional_32_addr = (hw_cap & GMAC_HW_FEAT_MACADR32SEL) >> 23;
+ dma_cap->additional_64_addr = (hw_cap & GMAC_HW_FEAT_MACADR64SEL) >> 24;
dma_cap->pcs = (hw_cap & GMAC_HW_FEAT_PCSSEL) >> 3;
dma_cap->sma_mdio = (hw_cap & GMAC_HW_FEAT_SMASEL) >> 5;
dma_cap->pmt_remote_wake_up = (hw_cap & GMAC_HW_FEAT_RWKSEL) >> 6;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 66ae365e1c89..d4ef05672aa6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -6582,6 +6582,18 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
seq_printf(seq,
"\tNumber of Additional MAC address registers: %d\n",
priv->dma_cap.multi_addr);
+ } else if (priv->plat->core_type == DWMAC_CORE_GMAC4) {
+ seq_printf(seq,
+ "\tNumber of MAC address registers (1-31): %d\n",
+ priv->dma_cap.multi_addr);
+ seq_printf(seq,
+ "\tAdditional 32 MAC address registers (32-63): %s\n",
+ priv->dma_cap.additional_32_addr ? "Y" : "N");
+ seq_printf(seq,
+ "\tAdditional 64 MAC address registers (64-127): %s\n",
+ priv->dma_cap.additional_64_addr ? "Y" : "N");
+ seq_printf(seq, "\tHash Filter: %s\n",
+ (priv->dma_cap.hash_filter) ? "Y" : "N");
} else {
seq_printf(seq, "\tHash Filter: %s\n",
(priv->dma_cap.hash_filter) ? "Y" : "N");
@@ -7484,6 +7496,33 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
priv->plat->tx_fifo_size = priv->dma_cap.tx_fifo_size;
}
+ /* On DWMAC4 we can get the exact number of perfect filter entries from
+ * the HW_Features.
+ */
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4) {
+ priv->hw->multi_addr = priv->dma_cap.multi_addr;
+ priv->hw->additional_32_addr =
+ !!priv->dma_cap.additional_32_addr;
+ priv->hw->additional_64_addr =
+ !!priv->dma_cap.additional_64_addr;
+
+ /* We always have one slot for the primary MAC */
+ priv->hw->unicast_filter_entries = 1;
+
+ /* How many slots in the 1 -> 31 range */
+ priv->hw->unicast_filter_entries += priv->hw->multi_addr;
+
+ /* Additional 32 entries in the 32 -> 63 range */
+ if (priv->hw->additional_32_addr)
+ priv->hw->unicast_filter_entries += 32;
+
+ /* Additional 64 entries in the 64 -> 127 range, can be enabled
+ * independently of the 32 -> 63 range
+ */
+ if (priv->hw->additional_64_addr)
+ priv->hw->unicast_filter_entries += 64;
+ }
+
priv->hw->vlan_fail_q_en =
(priv->plat->flags & STMMAC_FLAG_VLAN_FAIL_Q_EN);
priv->hw->vlan_fail_q = priv->plat->vlan_fail_q;