diff options
| author | Paolo Abeni <pabeni@redhat.com> | 2026-09-03 10:31:36 +0200 |
|---|---|---|
| committer | Paolo Abeni <pabeni@redhat.com> | 2026-09-03 10:31:36 +0200 |
| commit | 1ab75e8bbfbff3e93e0ea06f33bd970c5324d31c (patch) | |
| tree | 1c10747113f34f356f1af9dc7b474c1f05a005fb | |
| parent | e16acadb9a0f7a0f04318e4b3a8e89e8ddd0c905 (diff) | |
| parent | 6815415d68b98bd34d22aa0631c71b0fb7cbc7f5 (diff) | |
| download | linux-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.h | 5 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 6 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 60 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 39 |
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; |
