* [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload
@ 2026-11-05 13:19 Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG Daniel Machon
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Daniel Machon @ 2026-11-05 13:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
This series adds offload of tc matchall filters with a police action to
the ingress port policers of Sparx5 and lan969x, which are both served
by the sparx5 driver.
In hardware, each ingress port has four single-rate leaky bucket
policers. A policer drops the frames that exceed its rate and burst.
The four policers of a port share the pass and drop statistics counters
of the port.
The driver uses one policer per port, because with more than one, the
statistics reported to tc could not be attributed to the right policer.
This matches lan966x and ocelot. The policer meters each frame without
preamble and inter-frame gap, so the configured rate is a data rate, as
tc police expects and as on lan966x and ocelot.
Since the policer meters all ingress frames on the port, only filters on
chain 0 with protocol all are offloaded, and filters on shared blocks
are rejected. A police action that is already offloaded as a port
policer on another port is rejected too, since in software it is a
single bucket for all of its users. Only drop is supported as the exceed
action, and the police overhead parameter is not supported.
Patch #1 drops a redundant prefix from the existing POL_UPD_INT_CFG
register macros.
Patch #2 adds the registers used by the port policer.
Patch #3 tracks whether the ingress block of a port is shared, and adds
the per-port policer state.
Patch #4 adds the hardware programming of the port policer.
Patch #5 adds the tc matchall offload.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
Changes in v3:
- No changes, resubmitted because of missing sashiko-nipa check on v2.
- Link to v2: https://lore.kernel.org/r/20261102-b4-lan966x-pci-fdma-sparx5-lan969x-port-policers-v2-0-3bd47ec1b227@microchip.com
Changes in v2:
- Patch #4: meter the data rate, not the line rate, by setting the gap
to 0 instead of 20 bytes. Fix the commit message, which wrongly said
that lan966x and ocelot meter the line rate.
- Patch #4: take queue_stats_lock around the existing ethtool read of
port stat counter 0, since reading the LSB latches the MSB.
- Patch #4: correct the comment on the traffic type mask.
- Patch #4: reject a packets-per-second police as unsupported before
checking for a missing byte rate, so the right message is reported.
- Patch #5: reject a police action that is already offloaded as a port
policer on another port.
- Link to v1: https://lore.kernel.org/r/20261009-b4-lan966x-pci-fdma-sparx5-lan969x-port-policers-v1-0-4916708a7ea2@microchip.com
---
Daniel Machon (5):
net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG
net: sparx5: Add registers used by the port policer
net: sparx5: Track shared tc blocks and per-port policer state
net: sparx5: Add hardware programming for the port policer
net: sparx5: Offload tc port policers via matchall
.../microchip/sparx5/lan969x/lan969x_regs.c | 7 +
.../net/ethernet/microchip/sparx5/sparx5_ethtool.c | 6 +
.../net/ethernet/microchip/sparx5/sparx5_main.c | 6 +-
.../net/ethernet/microchip/sparx5/sparx5_main.h | 39 +-
.../ethernet/microchip/sparx5/sparx5_main_regs.h | 117 +++++-
.../net/ethernet/microchip/sparx5/sparx5_police.c | 431 ++++++++++++++++++++-
drivers/net/ethernet/microchip/sparx5/sparx5_qos.c | 4 +
.../net/ethernet/microchip/sparx5/sparx5_regs.c | 7 +
.../net/ethernet/microchip/sparx5/sparx5_regs.h | 7 +
drivers/net/ethernet/microchip/sparx5/sparx5_tc.c | 14 +-
.../ethernet/microchip/sparx5/sparx5_tc_matchall.c | 39 ++
11 files changed, 663 insertions(+), 14 deletions(-)
---
base-commit: 7b6ab4dbc156e2f96419715130b3d5738a306c93
change-id: 20260930-b4-lan966x-pci-fdma-sparx5-lan969x-port-policers-474610756e26
Best regards,
--
Daniel Machon <daniel.machon@microchip.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG
2026-11-05 13:19 [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload Daniel Machon
@ 2026-11-05 13:19 ` Daniel Machon
2026-11-05 17:16 ` Mohsin Bashir
2026-11-05 13:19 ` [PATCH net-next v3 2/5] net: sparx5: Add registers used by the port policer Daniel Machon
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Daniel Machon @ 2026-11-05 13:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
The macros for the ANA_AC_POL:POL_UPD_INT_CFG register are named
ANA_AC_POL_POL_UPD_INT_CFG*, repeating the POL_ prefix that is already
part of the target name.
Rename them to ANA_AC_POL_UPD_INT_CFG*, so that the port policer
registers added in the next patch can use the same short form.
No functional change.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 6 +++---
drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h | 12 ++++++------
2 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index e039f5369b95e..68c80c63f9087 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -616,10 +616,10 @@ static int sparx5_init_coreclock(struct sparx5 *sparx5)
sparx5,
HSCH_TAS_STATEMACHINE_CFG);
- spx5_rmw(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
- ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT,
+ spx5_rmw(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
+ ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT,
sparx5,
- ANA_AC_POL_POL_UPD_INT_CFG);
+ ANA_AC_POL_UPD_INT_CFG);
return 0;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
index 1464fd1a1d447..219c7f0e516e9 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
@@ -939,15 +939,15 @@ extern const struct sparx5_regs *regs;
FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x)
/* ANA_AC_POL:POL_ALL_CFG:POL_UPD_INT_CFG */
-#define ANA_AC_POL_POL_UPD_INT_CFG \
+#define ANA_AC_POL_UPD_INT_CFG \
__REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_ALL_CFG], \
0, 1, 1160, 1148, 0, 1, 4)
-#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT GENMASK(9, 0)
-#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(x)\
- FIELD_PREP(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT, x)
-#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_GET(x)\
- FIELD_GET(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT, x)
+#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT GENMASK(9, 0)
+#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_SET(x)\
+ FIELD_PREP(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT, x)
+#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_GET(x)\
+ FIELD_GET(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT, x)
/* ANA_AC_POL:COMMON_BDLB:DLB_CTRL */
#define ANA_AC_POL_BDLB_DLB_CTRL \
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 2/5] net: sparx5: Add registers used by the port policer
2026-11-05 13:19 [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG Daniel Machon
@ 2026-11-05 13:19 ` Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 3/5] net: sparx5: Track shared tc blocks and per-port policer state Daniel Machon
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Daniel Machon @ 2026-11-05 13:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
Add the registers used to configure the ingress port policers and to
read back their statistics, on both sparx5 and lan969x: the
POL_PORT_CFG and POL_PORT_CTRL groups of ANA_AC_POL, the shared
POL_ALL_CFG register, and the MSB part of the ANA_AC port statistics
counters.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
.../microchip/sparx5/lan969x/lan969x_regs.c | 7 ++
.../ethernet/microchip/sparx5/sparx5_main_regs.h | 105 +++++++++++++++++++++
.../net/ethernet/microchip/sparx5/sparx5_regs.c | 7 ++
.../net/ethernet/microchip/sparx5/sparx5_regs.h | 7 ++
4 files changed, 126 insertions(+)
diff --git a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
index 175fcf8bdd61c..5650550d4294d 100644
--- a/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
+++ b/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_regs.c
@@ -28,6 +28,9 @@ const unsigned int lan969x_rcnt[RCNT_LAST] = {
[RC_ANA_AC_OWN_UPSID] = 1,
[RC_ANA_ACL_VCAP_S2_CFG] = 35,
[RC_ANA_ACL_OWN_UPSID] = 1,
+ [RC_ANA_AC_POL_PORT_THRES_CFG_0] = 140,
+ [RC_ANA_AC_POL_PORT_THRES_CFG_1] = 140,
+ [RC_ANA_AC_POL_PORT_RATE_CFG] = 140,
[RC_ANA_CL_OWN_UPSID] = 1,
[RC_ANA_L2_OWN_UPSID] = 1,
[RC_ASM_PORT_CFG] = 32,
@@ -70,6 +73,8 @@ const unsigned int lan969x_gaddr[GADDR_LAST] = {
[GA_ANA_ACL_CNT_B] = 4096,
[GA_ANA_ACL_STICKY] = 10852,
[GA_ANA_AC_POL_POL_ALL_CFG] = 17504,
+ [GA_ANA_AC_POL_POL_PORT_CFG] = 0,
+ [GA_ANA_AC_POL_POL_PORT_CTRL] = 16384,
[GA_ANA_AC_POL_COMMON_BDLB] = 19464,
[GA_ANA_AC_POL_COMMON_BUM_SLB] = 19472,
[GA_ANA_AC_SDLB_LBGRP_TBL] = 31788,
@@ -121,6 +126,7 @@ const unsigned int lan969x_gcnt[GCNT_LAST] = {
[GC_ANA_ACL_KEY_SEL] = 99,
[GC_ANA_ACL_CNT_A] = 1024,
[GC_ANA_ACL_CNT_B] = 1024,
+ [GC_ANA_AC_POL_POL_PORT_CTRL] = 35,
[GC_ANA_AC_SDLB_LBGRP_TBL] = 5,
[GC_ANA_AC_SDLB_LBSET_TBL] = 496,
[GC_ANA_CL_PORT] = 35,
@@ -186,6 +192,7 @@ const unsigned int lan969x_fsize[FSIZE_LAST] = {
[FW_ANA_AC_TSN_SF_STATUS_TSN_SFID] = 8,
[FW_ANA_AC_SG_ACCESS_CTRL_SGID] = 8,
[FW_ANA_AC_PORT_SGE_CFG_MASK] = 17,
+ [FW_ANA_AC_POL_PORT_RATE_CFG_PORT_RATE] = 19,
[FW_ANA_AC_SDLB_XLB_START_LBSET_START] = 9,
[FW_ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT] = 3,
[FW_ANA_AC_SDLB_LBGRP_STATE_TBL_PUP_LBSET_NEXT] = 9,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
index 219c7f0e516e9..1bf7dabf622e6 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
@@ -554,6 +554,17 @@ extern const struct sparx5_regs *regs;
__REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_STAT_CNT_CFG_PORT], g,\
regs->gcnt[GC_ANA_AC_STAT_CNT_CFG_PORT], 64, 20, r, 4, 4)
+/* ANA_AC:STAT_CNT_CFG_PORT:STAT_MSB_CNT */
+#define ANA_AC_PORT_STAT_MSB_CNT(g, r) \
+ __REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_STAT_CNT_CFG_PORT], g,\
+ regs->gcnt[GC_ANA_AC_STAT_CNT_CFG_PORT], 64, 36, r, 4, 4)
+
+#define ANA_AC_PORT_STAT_MSB_CNT_MSB_CNT GENMASK(7, 0)
+#define ANA_AC_PORT_STAT_MSB_CNT_MSB_CNT_SET(x)\
+ FIELD_PREP(ANA_AC_PORT_STAT_MSB_CNT_MSB_CNT, x)
+#define ANA_AC_PORT_STAT_MSB_CNT_MSB_CNT_GET(x)\
+ FIELD_GET(ANA_AC_PORT_STAT_MSB_CNT_MSB_CNT, x)
+
/* ANA_AC:STAT_GLOBAL_CFG_ACL:GLOBAL_CNT_FRM_TYPE_CFG */
#define ANA_AC_ACL_GLOBAL_CNT_FRM_TYPE_CFG(r) \
__REG(TARGET_ANA_AC, 0, 1, regs->gaddr[GA_ANA_AC_STAT_GLOBAL_CFG_ACL], \
@@ -938,6 +949,35 @@ extern const struct sparx5_regs *regs;
#define ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY_GET(x)\
FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x)
+/* ANA_AC_POL:POL_ALL_CFG:POL_ALL_CFG */
+#define ANA_AC_POL_ALL_CFG \
+ __REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_ALL_CFG],\
+ 0, 1, 1160, 1144, 0, 1, 4)
+
+#define ANA_AC_POL_ALL_CFG_PORT_FC_ENA BIT(14)
+#define ANA_AC_POL_ALL_CFG_PORT_FC_ENA_SET(x)\
+ FIELD_PREP(ANA_AC_POL_ALL_CFG_PORT_FC_ENA, x)
+#define ANA_AC_POL_ALL_CFG_PORT_FC_ENA_GET(x)\
+ FIELD_GET(ANA_AC_POL_ALL_CFG_PORT_FC_ENA, x)
+
+#define ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT BIT(4)
+#define ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT_SET(x)\
+ FIELD_PREP(ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT, x)
+#define ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT_GET(x)\
+ FIELD_GET(ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT, x)
+
+#define ANA_AC_POL_ALL_CFG_LPORT_POLICE_ENA BIT(1)
+#define ANA_AC_POL_ALL_CFG_LPORT_POLICE_ENA_SET(x)\
+ FIELD_PREP(ANA_AC_POL_ALL_CFG_LPORT_POLICE_ENA, x)
+#define ANA_AC_POL_ALL_CFG_LPORT_POLICE_ENA_GET(x)\
+ FIELD_GET(ANA_AC_POL_ALL_CFG_LPORT_POLICE_ENA, x)
+
+#define ANA_AC_POL_ALL_CFG_FORCE_INIT BIT(0)
+#define ANA_AC_POL_ALL_CFG_FORCE_INIT_SET(x)\
+ FIELD_PREP(ANA_AC_POL_ALL_CFG_FORCE_INIT, x)
+#define ANA_AC_POL_ALL_CFG_FORCE_INIT_GET(x)\
+ FIELD_GET(ANA_AC_POL_ALL_CFG_FORCE_INIT, x)
+
/* ANA_AC_POL:POL_ALL_CFG:POL_UPD_INT_CFG */
#define ANA_AC_POL_UPD_INT_CFG \
__REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_ALL_CFG], \
@@ -949,6 +989,71 @@ extern const struct sparx5_regs *regs;
#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_GET(x)\
FIELD_GET(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT, x)
+/* ANA_AC_POL:POL_PORT_CFG:POL_PORT_THRES_CFG_0 */
+#define ANA_AC_POL_PORT_THRES_CFG_0(r) \
+ __REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_PORT_CFG],\
+ 0, 1, 8192, 0, r, regs->rcnt[RC_ANA_AC_POL_PORT_THRES_CFG_0], 4)
+
+#define ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0 GENMASK(5, 0)
+#define ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0_SET(x)\
+ FIELD_PREP(ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0, x)
+#define ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0_GET(x)\
+ FIELD_GET(ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0, x)
+
+/* ANA_AC_POL:POL_PORT_CFG:POL_PORT_THRES_CFG_1 */
+#define ANA_AC_POL_PORT_THRES_CFG_1(r) \
+ __REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_PORT_CFG],\
+ 0, 1, 8192, 2048, r,\
+ regs->rcnt[RC_ANA_AC_POL_PORT_THRES_CFG_1], 4)
+
+#define ANA_AC_POL_PORT_THRES_CFG_1_PORT_THRES1 GENMASK(5, 0)
+#define ANA_AC_POL_PORT_THRES_CFG_1_PORT_THRES1_SET(x)\
+ FIELD_PREP(ANA_AC_POL_PORT_THRES_CFG_1_PORT_THRES1, x)
+#define ANA_AC_POL_PORT_THRES_CFG_1_PORT_THRES1_GET(x)\
+ FIELD_GET(ANA_AC_POL_PORT_THRES_CFG_1_PORT_THRES1, x)
+
+/* ANA_AC_POL:POL_PORT_CFG:POL_PORT_RATE_CFG */
+#define ANA_AC_POL_PORT_RATE_CFG(r) \
+ __REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_PORT_CFG],\
+ 0, 1, 8192, 4096, r, regs->rcnt[RC_ANA_AC_POL_PORT_RATE_CFG], 4)
+
+#define ANA_AC_POL_PORT_RATE_CFG_PORT_RATE\
+ GENMASK(regs->fsize[FW_ANA_AC_POL_PORT_RATE_CFG_PORT_RATE] + 0 - 1, 0)
+#define ANA_AC_POL_PORT_RATE_CFG_PORT_RATE_SET(x)\
+ spx5_field_prep(ANA_AC_POL_PORT_RATE_CFG_PORT_RATE, x)
+#define ANA_AC_POL_PORT_RATE_CFG_PORT_RATE_GET(x)\
+ spx5_field_get(ANA_AC_POL_PORT_RATE_CFG_PORT_RATE, x)
+
+/* ANA_AC_POL:POL_PORT_CTRL:POL_PORT_GAP */
+#define ANA_AC_POL_PORT_GAP(g) \
+ __REG(TARGET_ANA_AC_POL, 0, 1,\
+ regs->gaddr[GA_ANA_AC_POL_POL_PORT_CTRL], g, \
+ regs->gcnt[GC_ANA_AC_POL_POL_PORT_CTRL], 32, 0, 0, 1, 4)
+
+#define ANA_AC_POL_PORT_GAP_PORT_PIPELINE_PT GENMASK(11, 7)
+#define ANA_AC_POL_PORT_GAP_PORT_PIPELINE_PT_SET(x)\
+ FIELD_PREP(ANA_AC_POL_PORT_GAP_PORT_PIPELINE_PT, x)
+#define ANA_AC_POL_PORT_GAP_PORT_PIPELINE_PT_GET(x)\
+ FIELD_GET(ANA_AC_POL_PORT_GAP_PORT_PIPELINE_PT, x)
+
+#define ANA_AC_POL_PORT_GAP_GAP_VALUE GENMASK(6, 0)
+#define ANA_AC_POL_PORT_GAP_GAP_VALUE_SET(x)\
+ FIELD_PREP(ANA_AC_POL_PORT_GAP_GAP_VALUE, x)
+#define ANA_AC_POL_PORT_GAP_GAP_VALUE_GET(x)\
+ FIELD_GET(ANA_AC_POL_PORT_GAP_GAP_VALUE, x)
+
+/* ANA_AC_POL:POL_PORT_CTRL:POL_PORT_CFG */
+#define ANA_AC_POL_PORT_CFG(g, r) \
+ __REG(TARGET_ANA_AC_POL, 0, 1,\
+ regs->gaddr[GA_ANA_AC_POL_POL_PORT_CTRL], g, \
+ regs->gcnt[GC_ANA_AC_POL_POL_PORT_CTRL], 32, 4, r, 4, 4)
+
+#define ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK GENMASK(7, 0)
+#define ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK_SET(x)\
+ FIELD_PREP(ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK, x)
+#define ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK_GET(x)\
+ FIELD_GET(ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK, x)
+
/* ANA_AC_POL:COMMON_BDLB:DLB_CTRL */
#define ANA_AC_POL_BDLB_DLB_CTRL \
__REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_COMMON_BDLB], \
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
index 75c1bd4f0053c..ce5f0bbf0e65b 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.c
@@ -28,6 +28,9 @@ const unsigned int sparx5_rcnt[RCNT_LAST] = {
[RC_ANA_AC_OWN_UPSID] = 3,
[RC_ANA_ACL_VCAP_S2_CFG] = 70,
[RC_ANA_ACL_OWN_UPSID] = 3,
+ [RC_ANA_AC_POL_PORT_THRES_CFG_0] = 280,
+ [RC_ANA_AC_POL_PORT_THRES_CFG_1] = 280,
+ [RC_ANA_AC_POL_PORT_RATE_CFG] = 280,
[RC_ANA_CL_OWN_UPSID] = 3,
[RC_ANA_L2_OWN_UPSID] = 3,
[RC_ASM_PORT_CFG] = 67,
@@ -70,6 +73,8 @@ const unsigned int sparx5_gaddr[GADDR_LAST] = {
[GA_ANA_ACL_CNT_B] = 16384,
[GA_ANA_ACL_STICKY] = 36408,
[GA_ANA_AC_POL_POL_ALL_CFG] = 75968,
+ [GA_ANA_AC_POL_POL_PORT_CFG] = 65536,
+ [GA_ANA_AC_POL_POL_PORT_CTRL] = 73728,
[GA_ANA_AC_POL_COMMON_BDLB] = 79048,
[GA_ANA_AC_POL_COMMON_BUM_SLB] = 79056,
[GA_ANA_AC_SDLB_LBGRP_TBL] = 295468,
@@ -121,6 +126,7 @@ const unsigned int sparx5_gcnt[GCNT_LAST] = {
[GC_ANA_ACL_KEY_SEL] = 134,
[GC_ANA_ACL_CNT_A] = 4096,
[GC_ANA_ACL_CNT_B] = 4096,
+ [GC_ANA_AC_POL_POL_PORT_CTRL] = 70,
[GC_ANA_AC_SDLB_LBGRP_TBL] = 10,
[GC_ANA_AC_SDLB_LBSET_TBL] = 4616,
[GC_ANA_CL_PORT] = 70,
@@ -186,6 +192,7 @@ const unsigned int sparx5_fsize[FSIZE_LAST] = {
[FW_ANA_AC_TSN_SF_STATUS_TSN_SFID] = 10,
[FW_ANA_AC_SG_ACCESS_CTRL_SGID] = 10,
[FW_ANA_AC_PORT_SGE_CFG_MASK] = 16,
+ [FW_ANA_AC_POL_PORT_RATE_CFG_PORT_RATE] = 20,
[FW_ANA_AC_SDLB_XLB_START_LBSET_START] = 13,
[FW_ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT] = 5,
[FW_ANA_AC_SDLB_LBGRP_STATE_TBL_PUP_LBSET_NEXT] = 13,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
index 97d496ebb7508..17abaa5798cbe 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_regs.h
@@ -36,6 +36,9 @@ enum sparx5_rcnt_enum {
RC_ANA_AC_OWN_UPSID,
RC_ANA_ACL_VCAP_S2_CFG,
RC_ANA_ACL_OWN_UPSID,
+ RC_ANA_AC_POL_PORT_THRES_CFG_0,
+ RC_ANA_AC_POL_PORT_THRES_CFG_1,
+ RC_ANA_AC_POL_PORT_RATE_CFG,
RC_ANA_CL_OWN_UPSID,
RC_ANA_L2_OWN_UPSID,
RC_ASM_PORT_CFG,
@@ -79,6 +82,8 @@ enum sparx5_gaddr_enum {
GA_ANA_ACL_CNT_B,
GA_ANA_ACL_STICKY,
GA_ANA_AC_POL_POL_ALL_CFG,
+ GA_ANA_AC_POL_POL_PORT_CFG,
+ GA_ANA_AC_POL_POL_PORT_CTRL,
GA_ANA_AC_POL_COMMON_BDLB,
GA_ANA_AC_POL_COMMON_BUM_SLB,
GA_ANA_AC_SDLB_LBGRP_TBL,
@@ -131,6 +136,7 @@ enum sparx5_gcnt_enum {
GC_ANA_ACL_KEY_SEL,
GC_ANA_ACL_CNT_A,
GC_ANA_ACL_CNT_B,
+ GC_ANA_AC_POL_POL_PORT_CTRL,
GC_ANA_AC_SDLB_LBGRP_TBL,
GC_ANA_AC_SDLB_LBSET_TBL,
GC_ANA_CL_PORT,
@@ -199,6 +205,7 @@ enum sparx5_fsize_enum {
FW_ANA_AC_TSN_SF_STATUS_TSN_SFID,
FW_ANA_AC_SG_ACCESS_CTRL_SGID,
FW_ANA_AC_PORT_SGE_CFG_MASK,
+ FW_ANA_AC_POL_PORT_RATE_CFG_PORT_RATE,
FW_ANA_AC_SDLB_XLB_START_LBSET_START,
FW_ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT,
FW_ANA_AC_SDLB_LBGRP_STATE_TBL_PUP_LBSET_NEXT,
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 3/5] net: sparx5: Track shared tc blocks and per-port policer state
2026-11-05 13:19 [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 2/5] net: sparx5: Add registers used by the port policer Daniel Machon
@ 2026-11-05 13:19 ` Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 4/5] net: sparx5: Add hardware programming for the port policer Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 5/5] net: sparx5: Offload tc port policers via matchall Daniel Machon
4 siblings, 0 replies; 8+ messages in thread
From: Daniel Machon @ 2026-11-05 13:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
A police action on a shared tc block applies one aggregate rate to all
ports bound to the block, which a per-port policer cannot implement.
The port policer offload therefore needs to know whether the ingress
block of a port is shared.
Add struct sparx5_port_tc to each port, holding a flag that records
whether the ingress block is shared, and the state of the four policers
of the port.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 16 ++++++++++++++++
drivers/net/ethernet/microchip/sparx5/sparx5_tc.c | 14 ++++++++++++--
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 8dc3a500c1c91..19217d443cc74 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -207,6 +207,21 @@ struct sparx5_port_config {
u32 sd_sgpio;
};
+#define SPX5_POLICERS_PER_PORT 4 /* Number of ingress port policers */
+
+struct sparx5_port_policer {
+ struct flow_stats prev;
+ struct flow_stats stats;
+ /* Port policers hold the client reference (cookie) */
+ unsigned long policer;
+};
+
+struct sparx5_port_tc {
+ /* Ingress uses a shared filter block */
+ bool ingress_block_shared;
+ struct sparx5_port_policer port_policer[SPX5_POLICERS_PER_PORT];
+};
+
struct sparx5_port {
struct net_device *ndev;
struct sparx5 *sparx5;
@@ -230,6 +245,7 @@ struct sparx5_port {
enum sparx5_vlan_port_type vlan_type;
u32 custom_etype;
bool vlan_aware;
+ struct sparx5_port_tc tc;
struct hrtimer inj_timer;
/* ptp */
u8 ptp_cmd;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c
index 30da5db57ef35..fd5a75aa09603 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc.c
@@ -47,7 +47,9 @@ static int sparx5_tc_block_cb_egress(enum tc_setup_type type,
static int sparx5_tc_setup_block(struct net_device *ndev,
struct flow_block_offload *fbo)
{
+ struct sparx5_port *port = netdev_priv(ndev);
flow_setup_cb_t *cb;
+ int err;
if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
cb = sparx5_tc_block_cb_ingress;
@@ -56,8 +58,16 @@ static int sparx5_tc_setup_block(struct net_device *ndev,
else
return -EOPNOTSUPP;
- return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
- cb, ndev, ndev, false);
+ err = flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
+ cb, ndev, ndev, false);
+ if (err)
+ return err;
+
+ if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+ port->tc.ingress_block_shared =
+ fbo->command == FLOW_BLOCK_BIND && fbo->block_shared;
+
+ return 0;
}
static void sparx5_tc_get_layer_and_idx(struct sparx5 *sparx5, u32 parent,
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 4/5] net: sparx5: Add hardware programming for the port policer
2026-11-05 13:19 [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload Daniel Machon
` (2 preceding siblings ...)
2026-11-05 13:19 ` [PATCH net-next v3 3/5] net: sparx5: Track shared tc blocks and per-port policer state Daniel Machon
@ 2026-11-05 13:19 ` Daniel Machon
2026-11-05 17:09 ` Mohsin Bashir
2026-11-05 13:19 ` [PATCH net-next v3 5/5] net: sparx5: Offload tc port policers via matchall Daniel Machon
4 siblings, 1 reply; 8+ messages in thread
From: Daniel Machon @ 2026-11-05 13:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
Each ingress port has four single-rate leaky bucket policers in
ANA_AC_POL. A policer is configured with a rate, in units of
25040 bit/s, and a burst, in units of 8192 bytes, and drops the frames
that exceed it. The four policers of a port share the pass and drop
statistics counters of the port, which are 40 bits wide. Before use,
all policers must be initialized through the force-init bits in
POL_ALL_CFG.
Add support for programming a port policer, and for reading back its
pass and drop counts. Do not include preamble and inter-frame gap in the
metered frame size, so that the configured rate is a data rate, as tc
police expects and as on lan966x and ocelot. Since the statistics
counters are shared, use only one policer per port. Initialize the
policers and select the events counted by the shared counters at probe.
Read the counters from the periodic stats worker as well, and accumulate
them into 64-bit totals, so that a wrap of the 40-bit counters is not
lost between two reads of the tc stats. Take queue_stats_lock around the
existing ethtool read of the shared counter 0, since reading the LSB of
a counter latches its MSB.
The port policer is not reachable from tc yet; the next patch adds
that.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
.../net/ethernet/microchip/sparx5/sparx5_ethtool.c | 6 +
.../net/ethernet/microchip/sparx5/sparx5_main.h | 22 +-
.../net/ethernet/microchip/sparx5/sparx5_police.c | 404 ++++++++++++++++++++-
drivers/net/ethernet/microchip/sparx5/sparx5_qos.c | 4 +
4 files changed, 432 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
index d42c57bead89d..9db3682afa955 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
@@ -231,9 +231,14 @@ static void sparx5_get_ana_ac_stats_stats(struct sparx5 *sparx5, int portno)
{
u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
+ /* Serialize with the port policer, which reads the LSB and MSB of the
+ * port stat counters as a pair.
+ */
+ mutex_lock(&sparx5->queue_stats_lock);
sparx5_update_counter(&portstats[spx5_stats_ana_ac_port_stat_lsb_cnt],
spx5_rd(sparx5, ANA_AC_PORT_STAT_LSB_CNT(portno,
SPX5_PORT_POLICER_DROPS)));
+ mutex_unlock(&sparx5->queue_stats_lock);
}
static void sparx5_get_dev_phy_stats(u64 *portstats, void __iomem *inst, u32
@@ -1116,6 +1121,7 @@ static void sparx5_update_port_stats(struct sparx5 *sparx5, int portno)
sparx5_get_asm_stats(sparx5, portno);
sparx5_get_ana_ac_stats_stats(sparx5, portno);
sparx5_get_queue_sys_stats(sparx5, portno);
+ sparx5_port_policer_stats_poll(sparx5->ports[portno]);
}
static void sparx5_update_stats(struct sparx5 *sparx5)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 19217d443cc74..93df90a62a9c8 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -19,6 +19,7 @@
#include <linux/hrtimer.h>
#include <linux/debugfs.h>
#include <net/flow_offload.h>
+#include <net/pkt_cls.h>
#include <fdma_api.h>
@@ -210,8 +211,11 @@ struct sparx5_port_config {
#define SPX5_POLICERS_PER_PORT 4 /* Number of ingress port policers */
struct sparx5_port_policer {
- struct flow_stats prev;
+ /* Last raw values of the 40-bit hardware counters */
+ struct flow_stats raw;
+ /* 64-bit totals, and the totals last reported to tc */
struct flow_stats stats;
+ struct flow_stats prev;
/* Port policers hold the client reference (cookie) */
unsigned long policer;
};
@@ -298,6 +302,10 @@ struct sparx5_mall_mirror_entry {
struct sparx5_port *port;
};
+struct sparx5_mall_port_policer_entry {
+ struct flow_action_police police;
+};
+
struct sparx5_mall_entry {
struct list_head list;
struct sparx5_port *port;
@@ -306,6 +314,7 @@ struct sparx5_mall_entry {
bool ingress;
union {
struct sparx5_mall_mirror_entry mirror;
+ struct sparx5_mall_port_policer_entry port_policer;
};
};
@@ -655,8 +664,8 @@ void sparx5_sdlb_group_init(struct sparx5 *sparx5, u64 max_rate, u32 min_burst,
/* sparx5_police.c */
enum {
- /* More policer types will be added later */
- SPX5_POL_SERVICE
+ SPX5_POL_SERVICE,
+ SPX5_POL_PORT,
};
struct sparx5_policer {
@@ -669,6 +678,13 @@ struct sparx5_policer {
};
int sparx5_policer_conf_set(struct sparx5 *sparx5, struct sparx5_policer *pol);
+int sparx5_port_policer_init(struct sparx5 *sparx5);
+int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
+ struct netlink_ext_ack *extack);
+int sparx5_delete_port_policer(struct sparx5_mall_entry *entry);
+int sparx5_update_port_policer_stats(struct net_device *ndev,
+ struct tc_cls_matchall_offload *tmo);
+void sparx5_port_policer_stats_poll(struct sparx5_port *port);
/* sparx5_psfp.c */
#define SPX5_PSFP_GCE_CNT 4
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_police.c b/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
index c88820e83812c..0fb1003e1b189 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
@@ -4,9 +4,47 @@
* Copyright (c) 2023 Microchip Technology Inc. and its subsidiaries.
*/
+#include <linux/iopoll.h>
+
#include "sparx5_main_regs.h"
#include "sparx5_main.h"
+#define SPX5_PORT_POLICER_FILTER_COUNTER 1
+#define SPX5_PORT_POLICER_PASS_COUNTER 2
+
+/* The port policer statistics counters are 40 bits wide (32-bit LSB + 8-bit
+ * MSB register pair).
+ */
+#define SPX5_PORT_STAT_CNT_MASK GENMASK_ULL(39, 0)
+
+#define SPX5_PORT_POLICER_0_FILTER_EVENT BIT(4)
+#define SPX5_PORT_POLICER_1_FILTER_EVENT BIT(5)
+#define SPX5_PORT_POLICER_2_FILTER_EVENT BIT(6)
+#define SPX5_PORT_POLICER_3_FILTER_EVENT BIT(7)
+#define SPX5_PORT_POLICER_0_PASS_EVENT BIT(8)
+#define SPX5_PORT_POLICER_1_PASS_EVENT BIT(9)
+#define SPX5_PORT_POLICER_2_PASS_EVENT BIT(10)
+#define SPX5_PORT_POLICER_3_PASS_EVENT BIT(11)
+
+/* Do not add preamble and inter-frame gap to the metered frame size, so that
+ * the rate is a data rate, as tc police expects.
+ */
+#define SPX5_POLICER_DATA_RATE_GAP 0
+
+/* Bit rate unit for the port policer (bits/sec) */
+#define SPX5_POLICER_RATE_UNIT 25040
+/* Burst size unit for the port policer (bytes) */
+#define SPX5_POLICER_BYTE_BURST_UNIT 8192
+
+enum sparx5_port_policer_stat_event_mask {
+ SPX5_PPEM_NONE,
+ SPX5_PPEM_EVENT_NO_ERROR,
+ SPX5_PPEM_EVENT_AND_ERROR,
+ SPX5_PPEM_EVENT,
+ SPX5_PPEM_ERROR_NO_EVENT,
+ SPX5_PPEM_ERROR,
+};
+
static int sparx5_policer_service_conf_set(struct sparx5 *sparx5,
struct sparx5_policer *pol)
{
@@ -40,10 +78,65 @@ static int sparx5_policer_service_conf_set(struct sparx5 *sparx5,
return 0;
}
+static int sparx5_port_policer_conf_set(struct sparx5 *sparx5,
+ struct sparx5_policer *pol)
+{
+ u32 portno = pol->idx / SPX5_POLICERS_PER_PORT;
+ u32 polidx = pol->idx % SPX5_POLICERS_PER_PORT;
+ u32 rate, burst, mask, cfg;
+ int cnt;
+
+ rate = DIV_ROUND_UP_ULL(pol->rate, SPX5_POLICER_RATE_UNIT);
+ burst = DIV_ROUND_UP(pol->burst, SPX5_POLICER_BYTE_BURST_UNIT);
+
+ /* Disable the policer when rate and burst are both zero, otherwise
+ * police known and unknown unicast, multicast and broadcast frames.
+ */
+ mask = (rate == 0 && burst == 0) ? 0 : 0x7f;
+ if (mask) {
+ /* Program the bucket before enabling the policer */
+ spx5_wr(rate, sparx5, ANA_AC_POL_PORT_RATE_CFG(pol->idx));
+ spx5_wr(burst, sparx5, ANA_AC_POL_PORT_THRES_CFG_0(pol->idx));
+ spx5_rmw(ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK_SET(mask),
+ ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK,
+ sparx5, ANA_AC_POL_PORT_CFG(portno, polidx));
+ } else {
+ /* Disable the policer before zeroing the bucket */
+ spx5_rmw(ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK_SET(0),
+ ANA_AC_POL_PORT_CFG_TRAFFIC_TYPE_MASK,
+ sparx5, ANA_AC_POL_PORT_CFG(portno, polidx));
+ spx5_wr(rate, sparx5, ANA_AC_POL_PORT_RATE_CFG(pol->idx));
+ spx5_wr(burst, sparx5, ANA_AC_POL_PORT_THRES_CFG_0(pol->idx));
+ }
+
+ /* Count policer events, not bytes, for all eight priorities on the
+ * port's filter and pass counters, which are shared by all of the
+ * port's policer instances.
+ */
+ cfg = ANA_AC_PORT_STAT_CFG_CFG_PRIO_MASK_SET(0xff) |
+ ANA_AC_PORT_STAT_CFG_CFG_CNT_FRM_TYPE_SET(SPX5_PPEM_EVENT) |
+ ANA_AC_PORT_STAT_CFG_CFG_CNT_BYTE_SET(0);
+
+ for (cnt = SPX5_PORT_POLICER_FILTER_COUNTER;
+ cnt <= SPX5_PORT_POLICER_PASS_COUNTER; cnt++)
+ spx5_rmw(cfg,
+ ANA_AC_PORT_STAT_CFG_CFG_PRIO_MASK |
+ ANA_AC_PORT_STAT_CFG_CFG_CNT_FRM_TYPE |
+ ANA_AC_PORT_STAT_CFG_CFG_CNT_BYTE,
+ sparx5, ANA_AC_PORT_STAT_CFG(portno, cnt));
+
+ spx5_rmw(ANA_AC_POL_PORT_GAP_GAP_VALUE_SET(SPX5_POLICER_DATA_RATE_GAP),
+ ANA_AC_POL_PORT_GAP_GAP_VALUE,
+ sparx5, ANA_AC_POL_PORT_GAP(portno));
+
+ return 0;
+}
+
int sparx5_policer_conf_set(struct sparx5 *sparx5, struct sparx5_policer *pol)
{
- /* More policer types will be added later */
switch (pol->type) {
+ case SPX5_POL_PORT:
+ return sparx5_port_policer_conf_set(sparx5, pol);
case SPX5_POL_SERVICE:
return sparx5_policer_service_conf_set(sparx5, pol);
default:
@@ -52,3 +145,312 @@ int sparx5_policer_conf_set(struct sparx5 *sparx5, struct sparx5_policer *pol)
return 0;
}
+
+static u64 sparx5_port_policer_cnt_get(struct sparx5_port *port, u32 cnt)
+{
+ struct sparx5 *sparx5 = port->sparx5;
+ u32 lsb, msb;
+
+ /* The MSB is latched when the LSB is read, so read the LSB first */
+ lsb = spx5_rd(sparx5, ANA_AC_PORT_STAT_LSB_CNT(port->portno, cnt));
+ msb = spx5_rd(sparx5, ANA_AC_PORT_STAT_MSB_CNT(port->portno, cnt));
+
+ return (u64)msb << 32 | lsb;
+}
+
+static void sparx5_port_policer_cnt_accum(struct sparx5_port *port, u32 cnt,
+ u64 *raw, u64 *total)
+{
+ u64 cur = sparx5_port_policer_cnt_get(port, cnt);
+
+ *total += (cur - *raw) & SPX5_PORT_STAT_CNT_MASK;
+ *raw = cur;
+}
+
+static void sparx5_port_policer_stats_update(struct sparx5_port *port,
+ int polidx)
+{
+ struct sparx5_port_policer *pp = &port->tc.port_policer[polidx];
+
+ lockdep_assert_held(&port->sparx5->queue_stats_lock);
+
+ sparx5_port_policer_cnt_accum(port, SPX5_PORT_POLICER_FILTER_COUNTER,
+ &pp->raw.drops, &pp->stats.drops);
+ sparx5_port_policer_cnt_accum(port, SPX5_PORT_POLICER_PASS_COUNTER,
+ &pp->raw.pkts, &pp->stats.pkts);
+}
+
+static void sparx5_policer_stats_update(struct sparx5 *sparx5,
+ struct sparx5_policer *pol)
+{
+ u32 portno, polidx;
+
+ switch (pol->type) {
+ case SPX5_POL_PORT:
+ portno = pol->idx / SPX5_POLICERS_PER_PORT;
+ polidx = pol->idx % SPX5_POLICERS_PER_PORT;
+
+ sparx5_port_policer_stats_update(sparx5->ports[portno], polidx);
+ break;
+ default:
+ break;
+ }
+}
+
+static int sparx5_get_port_policer_idx(struct sparx5_port *port,
+ unsigned long cookie)
+{
+ if (port->tc.port_policer[0].policer == cookie)
+ return 0;
+
+ return -ENOENT;
+}
+
+static int sparx5_alloc_port_policer_idx(struct sparx5_port *port,
+ unsigned long cookie)
+{
+ /* Only one port policer per port is supported: the port's filter
+ * and pass statistics counters are shared by all four hardware
+ * policer instances, so stats can only be attributed correctly to
+ * a single active policer.
+ */
+ if (!port->tc.port_policer[0].policer ||
+ port->tc.port_policer[0].policer == cookie)
+ return 0;
+
+ return -EEXIST;
+}
+
+static void sparx5_get_port_policer_stats(struct sparx5_port *port,
+ int polidx)
+{
+ struct sparx5_policer pol = { 0 };
+
+ pol.type = SPX5_POL_PORT;
+ pol.idx = port->portno * SPX5_POLICERS_PER_PORT + polidx;
+
+ sparx5_policer_stats_update(port->sparx5, &pol);
+}
+
+static void sparx5_init_port_policer_stats(struct sparx5_port *port,
+ int polidx)
+{
+ struct sparx5_port_policer *pp = &port->tc.port_policer[polidx];
+
+ mutex_lock(&port->sparx5->queue_stats_lock);
+
+ pp->raw.drops = sparx5_port_policer_cnt_get(port,
+ SPX5_PORT_POLICER_FILTER_COUNTER);
+ pp->raw.pkts = sparx5_port_policer_cnt_get(port,
+ SPX5_PORT_POLICER_PASS_COUNTER);
+
+ memset(&pp->stats, 0, sizeof(pp->stats));
+ memset(&pp->prev, 0, sizeof(pp->prev));
+
+ mutex_unlock(&port->sparx5->queue_stats_lock);
+}
+
+/* Called once a second from the stats worker, so that a wrap of the 40-bit
+ * counters is never missed. Only one port policer per port is used.
+ */
+void sparx5_port_policer_stats_poll(struct sparx5_port *port)
+{
+ mutex_lock(&port->sparx5->queue_stats_lock);
+ sparx5_get_port_policer_stats(port, 0);
+ mutex_unlock(&port->sparx5->queue_stats_lock);
+}
+
+int sparx5_update_port_policer_stats(struct net_device *ndev,
+ struct tc_cls_matchall_offload *tmo)
+{
+ struct sparx5_port *port = netdev_priv(ndev);
+ struct flow_stats *prev_stats;
+ struct flow_stats *stats;
+ u64 pkts, drops;
+ int polidx;
+
+ polidx = sparx5_get_port_policer_idx(port, tmo->cookie);
+ if (polidx < 0)
+ return polidx;
+
+ stats = &port->tc.port_policer[polidx].stats;
+ prev_stats = &port->tc.port_policer[polidx].prev;
+
+ mutex_lock(&port->sparx5->queue_stats_lock);
+
+ sparx5_get_port_policer_stats(port, polidx);
+
+ pkts = stats->pkts - prev_stats->pkts;
+ drops = stats->drops - prev_stats->drops;
+
+ prev_stats->pkts = stats->pkts;
+ prev_stats->drops = stats->drops;
+
+ mutex_unlock(&port->sparx5->queue_stats_lock);
+
+ if (!pkts && !drops)
+ return 0;
+
+ flow_stats_update(&tmo->stats,
+ 0,
+ pkts + drops,
+ drops,
+ jiffies,
+ FLOW_ACTION_HW_STATS_IMMEDIATE);
+
+ return 0;
+}
+
+int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
+ struct netlink_ext_ack *extack)
+{
+ struct flow_action_police *police = &entry->port_policer.police;
+ struct sparx5_port *port = entry->port;
+ struct sparx5 *sparx5 = port->sparx5;
+ struct sparx5_policer pol = { 0 };
+ int idx, err;
+
+ if (!entry->ingress) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer is not supported on egress");
+ return -EOPNOTSUPP;
+ }
+
+ if (entry->port->tc.ingress_block_shared) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer is not supported on shared ingress blocks");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->exceed.act_id != FLOW_ACTION_DROP) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer parameters are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->notexceed.act_id != FLOW_ACTION_PIPE &&
+ police->notexceed.act_id != FLOW_ACTION_ACCEPT) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer parameters are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->peakrate_bytes_ps || police->avrate ||
+ police->overhead) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer parameters are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->rate_pkt_ps) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer parameters are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (!police->rate_bytes_ps) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer requires a rate");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->burst > ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0 *
+ SPX5_POLICER_BYTE_BURST_UNIT) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer parameters are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (police->rate_bytes_ps >
+ div_u64((u64)ANA_AC_POL_PORT_RATE_CFG_PORT_RATE *
+ SPX5_POLICER_RATE_UNIT, BITS_PER_BYTE)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Policer parameters are not supported");
+ return -EOPNOTSUPP;
+ }
+
+ idx = sparx5_alloc_port_policer_idx(port, entry->cookie);
+ if (idx < 0) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only one port policer per port is supported");
+ return idx;
+ }
+
+ pol.type = SPX5_POL_PORT;
+ pol.rate = police->rate_bytes_ps * BITS_PER_BYTE;
+ pol.burst = police->burst;
+ pol.idx = port->portno * SPX5_POLICERS_PER_PORT + idx;
+
+ sparx5_init_port_policer_stats(port, idx);
+
+ err = sparx5_policer_conf_set(sparx5, &pol);
+ if (err)
+ return err;
+
+ port->tc.port_policer[idx].policer = entry->cookie;
+
+ return 0;
+}
+
+int sparx5_delete_port_policer(struct sparx5_mall_entry *entry)
+{
+ struct sparx5_port *port = entry->port;
+ struct sparx5 *sparx5 = port->sparx5;
+ struct sparx5_policer pol = { 0 };
+ int idx, err;
+
+ if (!entry->ingress)
+ return -EINVAL;
+
+ idx = sparx5_get_port_policer_idx(port, entry->cookie);
+ if (idx < 0)
+ return idx;
+
+ pol.type = SPX5_POL_PORT;
+ pol.idx = port->portno * SPX5_POLICERS_PER_PORT + idx;
+
+ err = sparx5_policer_conf_set(sparx5, &pol);
+ if (err)
+ return err;
+
+ port->tc.port_policer[idx].policer = 0;
+
+ return 0;
+}
+
+int sparx5_port_policer_init(struct sparx5 *sparx5)
+{
+ u32 value;
+ int err;
+
+ /* Initialize all port policers before usage */
+ spx5_rmw(ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT_SET(1) |
+ ANA_AC_POL_ALL_CFG_FORCE_INIT_SET(1),
+ ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT |
+ ANA_AC_POL_ALL_CFG_FORCE_INIT,
+ sparx5, ANA_AC_POL_ALL_CFG);
+
+ err = read_poll_timeout(spx5_rd, value,
+ !(ANA_AC_POL_ALL_CFG_ACL_FORCE_INIT_GET(value) |
+ ANA_AC_POL_ALL_CFG_FORCE_INIT_GET(value)),
+ 500, 10000, false, sparx5, ANA_AC_POL_ALL_CFG);
+ if (err)
+ return err;
+
+ /* Configure the port policer filter and pass statistics counters.
+ * Counter 0 is owned by sparx5_ethtool.c (SPX5_PORT_POLICER_DROPS).
+ */
+ spx5_wr(SPX5_PORT_POLICER_0_FILTER_EVENT |
+ SPX5_PORT_POLICER_1_FILTER_EVENT |
+ SPX5_PORT_POLICER_2_FILTER_EVENT |
+ SPX5_PORT_POLICER_3_FILTER_EVENT,
+ sparx5, ANA_AC_PORT_SGE_CFG(SPX5_PORT_POLICER_FILTER_COUNTER));
+
+ spx5_wr(SPX5_PORT_POLICER_0_PASS_EVENT |
+ SPX5_PORT_POLICER_1_PASS_EVENT |
+ SPX5_PORT_POLICER_2_PASS_EVENT |
+ SPX5_PORT_POLICER_3_PASS_EVENT,
+ sparx5, ANA_AC_PORT_SGE_CFG(SPX5_PORT_POLICER_PASS_COUNTER));
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c
index 7370533243b6b..06412078bc651 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_qos.c
@@ -449,6 +449,10 @@ int sparx5_qos_init(struct sparx5 *sparx5)
{
int ret;
+ ret = sparx5_port_policer_init(sparx5);
+ if (ret)
+ return ret;
+
ret = sparx5_leak_groups_init(sparx5);
if (ret < 0)
return ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net-next v3 5/5] net: sparx5: Offload tc port policers via matchall
2026-11-05 13:19 [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload Daniel Machon
` (3 preceding siblings ...)
2026-11-05 13:19 ` [PATCH net-next v3 4/5] net: sparx5: Add hardware programming for the port policer Daniel Machon
@ 2026-11-05 13:19 ` Daniel Machon
4 siblings, 0 replies; 8+ messages in thread
From: Daniel Machon @ 2026-11-05 13:19 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
Offload a tc matchall filter with a police action to the port policer:
tc qdisc add dev <eth> clsact
tc filter add dev <eth> ingress matchall skip_sw \
action police rate <bps> burst <bytes> conform-exceed drop
The port policer meters all ingress frames on the port, so offload the
filter only on ingress, on chain 0, with protocol all, and on a block
that is not shared. Only drop is supported as the exceed action, and
only one policer per port. A police action shared between ports is a
single bucket in software, which the per-port policers cannot express,
so reject a police action that is already offloaded as a port policer on
another port. Report the pass and drop counts through the matchall
stats.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
.../net/ethernet/microchip/sparx5/sparx5_main.h | 3 ++
.../net/ethernet/microchip/sparx5/sparx5_police.c | 27 +++++++++++++++
.../ethernet/microchip/sparx5/sparx5_tc_matchall.c | 39 ++++++++++++++++++++++
3 files changed, 69 insertions(+)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 93df90a62a9c8..ad29db91ce91e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -218,6 +218,8 @@ struct sparx5_port_policer {
struct flow_stats prev;
/* Port policers hold the client reference (cookie) */
unsigned long policer;
+ /* tc police action, the same for every filter bound to it */
+ unsigned long act_cookie;
};
struct sparx5_port_tc {
@@ -304,6 +306,7 @@ struct sparx5_mall_mirror_entry {
struct sparx5_mall_port_policer_entry {
struct flow_action_police police;
+ unsigned long act_cookie;
};
struct sparx5_mall_entry {
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_police.c b/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
index 0fb1003e1b189..a635b95a7bd73 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_police.c
@@ -301,6 +301,25 @@ int sparx5_update_port_policer_stats(struct net_device *ndev,
return 0;
}
+static bool sparx5_port_policer_act_in_use(struct sparx5_port *port,
+ unsigned long act_cookie)
+{
+ struct sparx5 *sparx5 = port->sparx5;
+ struct sparx5_port *other;
+ int i;
+
+ for (i = 0; i < sparx5->data->consts->n_ports; i++) {
+ other = sparx5->ports[i];
+ if (!other || other == port)
+ continue;
+ if (other->tc.port_policer[0].policer &&
+ other->tc.port_policer[0].act_cookie == act_cookie)
+ return true;
+ }
+
+ return false;
+}
+
int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
struct netlink_ext_ack *extack)
{
@@ -322,6 +341,13 @@ int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
return -EOPNOTSUPP;
}
+ if (sparx5_port_policer_act_in_use(port,
+ entry->port_policer.act_cookie)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Police action shared between ports is not supported");
+ return -EOPNOTSUPP;
+ }
+
if (police->exceed.act_id != FLOW_ACTION_DROP) {
NL_SET_ERR_MSG_MOD(extack,
"Policer parameters are not supported");
@@ -388,6 +414,7 @@ int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
return err;
port->tc.port_policer[idx].policer = entry->cookie;
+ port->tc.port_policer[idx].act_cookie = entry->port_policer.act_cookie;
return 0;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
index 146d7f28e8f3d..4961b49fe1831 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_matchall.c
@@ -43,6 +43,14 @@ sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry *entry,
entry->mirror.port = netdev_priv(action->dev);
}
+static void
+sparx5_tc_matchall_parse_port_policer_action(struct sparx5_mall_entry *entry,
+ struct flow_action_entry *action)
+{
+ entry->port_policer.police = action->police;
+ entry->port_policer.act_cookie = action->cookie;
+}
+
static int sparx5_tc_matchall_replace(struct net_device *ndev,
struct tc_cls_matchall_offload *tmo,
bool ingress)
@@ -98,6 +106,30 @@ static int sparx5_tc_matchall_replace(struct net_device *ndev,
}
/* Get baseline stats for this port */
sparx5_mirror_stats(mall_entry, &tmo->stats);
+ break;
+ case FLOW_ACTION_POLICE:
+ if (tmo->common.protocol != htons(ETH_P_ALL)) {
+ NL_SET_ERR_MSG_MOD(tmo->common.extack,
+ "Policer is only supported with protocol all");
+ kfree(mall_entry);
+ return -EOPNOTSUPP;
+ }
+
+ if (tmo->common.chain_index) {
+ NL_SET_ERR_MSG_MOD(tmo->common.extack,
+ "Policer is only supported on chain 0");
+ kfree(mall_entry);
+ return -EOPNOTSUPP;
+ }
+
+ sparx5_tc_matchall_parse_port_policer_action(mall_entry,
+ action);
+ err = sparx5_add_port_policer(mall_entry, tmo->common.extack);
+ if (err) {
+ kfree(mall_entry);
+ return err;
+ }
+
break;
case FLOW_ACTION_GOTO:
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
@@ -151,6 +183,11 @@ static int sparx5_tc_matchall_destroy(struct net_device *ndev,
if (entry->type == FLOW_ACTION_MIRRED) {
sparx5_mirror_del(entry);
+ } else if (entry->type == FLOW_ACTION_POLICE) {
+ err = sparx5_delete_port_policer(entry);
+ if (err)
+ NL_SET_ERR_MSG_MOD(tmo->common.extack,
+ "Could not delete port policer");
} else if (entry->type == FLOW_ACTION_GOTO) {
err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
0, 0, tmo->cookie, false);
@@ -180,6 +217,8 @@ static int sparx5_tc_matchall_stats(struct net_device *ndev,
if (entry->type == FLOW_ACTION_MIRRED) {
sparx5_mirror_stats(entry, &tmo->stats);
+ } else if (entry->type == FLOW_ACTION_POLICE) {
+ sparx5_update_port_policer_stats(ndev, tmo);
} else {
NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
return -EOPNOTSUPP;
--
2.34.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 4/5] net: sparx5: Add hardware programming for the port policer
2026-11-05 13:19 ` [PATCH net-next v3 4/5] net: sparx5: Add hardware programming for the port policer Daniel Machon
@ 2026-11-05 17:09 ` Mohsin Bashir
0 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-11-05 17:09 UTC (permalink / raw)
To: Daniel Machon, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
> +int sparx5_add_port_policer(struct sparx5_mall_entry *entry,
> + struct netlink_ext_ack *extack)
> +{
> + struct flow_action_police *police = &entry->port_policer.police;
> + struct sparx5_port *port = entry->port;
> + struct sparx5 *sparx5 = port->sparx5;
> + struct sparx5_policer pol = { 0 };
> + int idx, err;
> +
> + if (!entry->ingress) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer is not supported on egress");
> + return -EOPNOTSUPP;
> + }
> +
> + if (entry->port->tc.ingress_block_shared) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer is not supported on shared ingress blocks");
> + return -EOPNOTSUPP;
> + }
> +
> + if (police->exceed.act_id != FLOW_ACTION_DROP) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer parameters are not supported");
> + return -EOPNOTSUPP;
> + }
> +
> + if (police->notexceed.act_id != FLOW_ACTION_PIPE &&
> + police->notexceed.act_id != FLOW_ACTION_ACCEPT) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer parameters are not supported");
> + return -EOPNOTSUPP;
> + }
> +
> + if (police->peakrate_bytes_ps || police->avrate ||
> + police->overhead) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer parameters are not supported");
> + return -EOPNOTSUPP;
> + }
> +
> + if (police->rate_pkt_ps) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer parameters are not supported");
> + return -EOPNOTSUPP;
> + }
Curious if we can have distinct messages for several unrelated checks
sharing the extack "Policer parameters are not supported";
> +
> + if (!police->rate_bytes_ps) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer requires a rate");
> + return -EOPNOTSUPP;
> + }
> +
> + if (police->burst > ANA_AC_POL_PORT_THRES_CFG_0_PORT_THRES0 *
> + SPX5_POLICER_BYTE_BURST_UNIT) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer parameters are not supported");
> + return -EOPNOTSUPP;
> + }
> +
> + if (police->rate_bytes_ps >
> + div_u64((u64)ANA_AC_POL_PORT_RATE_CFG_PORT_RATE *
> + SPX5_POLICER_RATE_UNIT, BITS_PER_BYTE)) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Policer parameters are not supported");
> + return -EOPNOTSUPP;
> + }
> +
> + idx = sparx5_alloc_port_policer_idx(port, entry->cookie);
> + if (idx < 0) {
> + NL_SET_ERR_MSG_MOD(extack,
> + "Only one port policer per port is supported");
> + return idx;
> + }
> +
Any reason why police->mtu isn't checked? In software, frames longer
than the police MTU always take the exceed action. Is this intentionally
skipped here?
> + pol.type = SPX5_POL_PORT;
> + pol.rate = police->rate_bytes_ps * BITS_PER_BYTE;
> + pol.burst = police->burst;
> + pol.idx = port->portno * SPX5_POLICERS_PER_PORT + idx;
> +
> + sparx5_init_port_policer_stats(port, idx);
> +
> + err = sparx5_policer_conf_set(sparx5, &pol);
> + if (err)
> + return err;
> +
> + port->tc.port_policer[idx].policer = entry->cookie;
> +
> + return 0;
> +}
> +
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG
2026-11-05 13:19 ` [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG Daniel Machon
@ 2026-11-05 17:16 ` Mohsin Bashir
0 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-11-05 17:16 UTC (permalink / raw)
To: Daniel Machon, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Steen Hegelund, UNGLinuxDriver
Cc: robert.marko, netdev, linux-arm-kernel, linux-kernel
On 11/5/26 5:19 AM, Daniel Machon wrote:
> The macros for the ANA_AC_POL:POL_UPD_INT_CFG register are named
> ANA_AC_POL_POL_UPD_INT_CFG*, repeating the POL_ prefix that is already
> part of the target name.
>
> Rename them to ANA_AC_POL_UPD_INT_CFG*, so that the port policer
> registers added in the next patch can use the same short form.
>
> No functional change.
>
> Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
> ---
> drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 6 +++---
> drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h | 12 ++++++------
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
> index e039f5369b95e..68c80c63f9087 100644
> --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
> @@ -616,10 +616,10 @@ static int sparx5_init_coreclock(struct sparx5 *sparx5)
> sparx5,
> HSCH_TAS_STATEMACHINE_CFG);
>
> - spx5_rmw(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
> - ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT,
> + spx5_rmw(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
> + ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT,
> sparx5,
> - ANA_AC_POL_POL_UPD_INT_CFG);
> + ANA_AC_POL_UPD_INT_CFG);
>
> return 0;
> }
> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> index 1464fd1a1d447..219c7f0e516e9 100644
> --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
> @@ -939,15 +939,15 @@ extern const struct sparx5_regs *regs;
> FIELD_GET(ANA_ACL_SEC_LOOKUP_STICKY_SEC_TYPE_MAC_ETYPE_STICKY, x)
>
> /* ANA_AC_POL:POL_ALL_CFG:POL_UPD_INT_CFG */
> -#define ANA_AC_POL_POL_UPD_INT_CFG \
> +#define ANA_AC_POL_UPD_INT_CFG \
> __REG(TARGET_ANA_AC_POL, 0, 1, regs->gaddr[GA_ANA_AC_POL_POL_ALL_CFG], \
> 0, 1, 1160, 1148, 0, 1, 4)
>
> -#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT GENMASK(9, 0)
> -#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(x)\
> - FIELD_PREP(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT, x)
> -#define ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_GET(x)\
> - FIELD_GET(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT, x)
> +#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT GENMASK(9, 0)
> +#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_SET(x)\
> + FIELD_PREP(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT, x)
> +#define ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT_GET(x)\
> + FIELD_GET(ANA_AC_POL_UPD_INT_CFG_POL_UPD_INT, x)
>
> /* ANA_AC_POL:COMMON_BDLB:DLB_CTRL */
> #define ANA_AC_POL_BDLB_DLB_CTRL \
>
Reviewed-by: Mohsin Bashir <hmohsin@meta.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-11-05 17:16 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 13:19 [PATCH net-next v3 0/5] net: sparx5: add tc matchall port policer offload Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 1/5] net: sparx5: Drop redundant POL_ prefix from POL_UPD_INT_CFG Daniel Machon
2026-11-05 17:16 ` Mohsin Bashir
2026-11-05 13:19 ` [PATCH net-next v3 2/5] net: sparx5: Add registers used by the port policer Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 3/5] net: sparx5: Track shared tc blocks and per-port policer state Daniel Machon
2026-11-05 13:19 ` [PATCH net-next v3 4/5] net: sparx5: Add hardware programming for the port policer Daniel Machon
2026-11-05 17:09 ` Mohsin Bashir
2026-11-05 13:19 ` [PATCH net-next v3 5/5] net: sparx5: Offload tc port policers via matchall Daniel Machon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox