1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
7 #include "sparx5_main.h"
8 #include "sparx5_main_regs.h"
11 #define SPX5_MIRROR_PROBE_MAX 3
12 #define SPX5_MIRROR_DISABLED 0
13 #define SPX5_MIRROR_EGRESS 1
14 #define SPX5_MIRROR_INGRESS 2
15 #define SPX5_MIRROR_MONITOR_PORT_DEFAULT 65
16 #define SPX5_QFWD_MP_OFFSET 9 /* Mirror port offset in the QFWD register */
18 /* Convert from bool ingress/egress to mirror direction */
19 static u32
sparx5_mirror_to_dir(bool ingress
)
21 return ingress
? SPX5_MIRROR_INGRESS
: SPX5_MIRROR_EGRESS
;
24 /* Get ports belonging to this mirror */
25 static u64
sparx5_mirror_port_get(struct sparx5
*sparx5
, u32 idx
)
29 val
= spx5_rd(sparx5
, ANA_AC_PROBE_PORT_CFG(idx
));
31 if (is_sparx5(sparx5
))
32 val
|= (u64
)spx5_rd(sparx5
, ANA_AC_PROBE_PORT_CFG1(idx
)) << 32;
37 /* Add port to mirror (only front ports) */
38 static void sparx5_mirror_port_add(struct sparx5
*sparx5
, u32 idx
, u32 portno
)
43 val
= BIT(do_div(reg
, 32));
46 return spx5_rmw(val
, val
, sparx5
, ANA_AC_PROBE_PORT_CFG(idx
));
48 return spx5_rmw(val
, val
, sparx5
, ANA_AC_PROBE_PORT_CFG1(idx
));
51 /* Delete port from mirror (only front ports) */
52 static void sparx5_mirror_port_del(struct sparx5
*sparx5
, u32 idx
, u32 portno
)
57 val
= BIT(do_div(reg
, 32));
60 return spx5_rmw(0, val
, sparx5
, ANA_AC_PROBE_PORT_CFG(idx
));
62 return spx5_rmw(0, val
, sparx5
, ANA_AC_PROBE_PORT_CFG1(idx
));
65 /* Check if mirror contains port */
66 static bool sparx5_mirror_contains(struct sparx5
*sparx5
, u32 idx
, u32 portno
)
68 return (sparx5_mirror_port_get(sparx5
, idx
) & BIT_ULL(portno
)) != 0;
71 /* Check if mirror is empty */
72 static bool sparx5_mirror_is_empty(struct sparx5
*sparx5
, u32 idx
)
74 return sparx5_mirror_port_get(sparx5
, idx
) == 0;
77 /* Get direction of mirror */
78 static u32
sparx5_mirror_dir_get(struct sparx5
*sparx5
, u32 idx
)
80 u32 val
= spx5_rd(sparx5
, ANA_AC_PROBE_CFG(idx
));
82 return ANA_AC_PROBE_CFG_PROBE_DIRECTION_GET(val
);
85 /* Set direction of mirror */
86 static void sparx5_mirror_dir_set(struct sparx5
*sparx5
, u32 idx
, u32 dir
)
88 spx5_rmw(ANA_AC_PROBE_CFG_PROBE_DIRECTION_SET(dir
),
89 ANA_AC_PROBE_CFG_PROBE_DIRECTION
, sparx5
,
90 ANA_AC_PROBE_CFG(idx
));
93 /* Set the monitor port for this mirror */
94 static void sparx5_mirror_monitor_set(struct sparx5
*sparx5
, u32 idx
,
97 spx5_rmw(QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_SET(portno
),
98 QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL
, sparx5
,
99 QFWD_FRAME_COPY_CFG(idx
+ SPX5_QFWD_MP_OFFSET
));
102 /* Get the monitor port of this mirror */
103 static u32
sparx5_mirror_monitor_get(struct sparx5
*sparx5
, u32 idx
)
105 u32 val
= spx5_rd(sparx5
,
106 QFWD_FRAME_COPY_CFG(idx
+ SPX5_QFWD_MP_OFFSET
));
108 return QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_GET(val
);
111 /* Check if port is the monitor port of this mirror */
112 static bool sparx5_mirror_has_monitor(struct sparx5
*sparx5
, u32 idx
,
115 return sparx5_mirror_monitor_get(sparx5
, idx
) == portno
;
118 /* Get a suitable mirror for this port */
119 static int sparx5_mirror_get(struct sparx5_port
*sport
,
120 struct sparx5_port
*mport
, u32 dir
, u32
*idx
)
122 struct sparx5
*sparx5
= sport
->sparx5
;
125 /* Check if this port is already used as a monitor port */
126 for (i
= 0; i
< SPX5_MIRROR_PROBE_MAX
; i
++)
127 if (sparx5_mirror_has_monitor(sparx5
, i
, sport
->portno
))
130 /* Check if existing mirror can be reused
131 * (same direction and monitor port).
133 for (i
= 0; i
< SPX5_MIRROR_PROBE_MAX
; i
++) {
134 if (sparx5_mirror_dir_get(sparx5
, i
) == dir
&&
135 sparx5_mirror_has_monitor(sparx5
, i
, mport
->portno
)) {
141 /* Return free mirror */
142 for (i
= 0; i
< SPX5_MIRROR_PROBE_MAX
; i
++) {
143 if (sparx5_mirror_is_empty(sparx5
, i
)) {
152 int sparx5_mirror_add(struct sparx5_mall_entry
*entry
)
154 u32 mirror_idx
, dir
= sparx5_mirror_to_dir(entry
->ingress
);
155 struct sparx5_port
*sport
, *mport
;
156 struct sparx5
*sparx5
;
162 mport
= entry
->mirror
.port
;
163 sparx5
= sport
->sparx5
;
165 if (sport
->portno
== mport
->portno
)
168 err
= sparx5_mirror_get(sport
, mport
, dir
, &mirror_idx
);
172 if (sparx5_mirror_contains(sparx5
, mirror_idx
, sport
->portno
))
175 /* Add port to mirror */
176 sparx5_mirror_port_add(sparx5
, mirror_idx
, sport
->portno
);
178 /* Set direction of mirror */
179 sparx5_mirror_dir_set(sparx5
, mirror_idx
, dir
);
181 /* Set monitor port for mirror */
182 sparx5_mirror_monitor_set(sparx5
, mirror_idx
, mport
->portno
);
184 entry
->mirror
.idx
= mirror_idx
;
189 void sparx5_mirror_del(struct sparx5_mall_entry
*entry
)
191 struct sparx5_port
*port
= entry
->port
;
192 struct sparx5
*sparx5
= port
->sparx5
;
193 u32 mirror_idx
= entry
->mirror
.idx
;
195 sparx5_mirror_port_del(sparx5
, mirror_idx
, port
->portno
);
196 if (!sparx5_mirror_is_empty(sparx5
, mirror_idx
))
199 sparx5_mirror_dir_set(sparx5
, mirror_idx
, SPX5_MIRROR_DISABLED
);
201 sparx5_mirror_monitor_set(sparx5
,
203 SPX5_MIRROR_MONITOR_PORT_DEFAULT
);
206 void sparx5_mirror_stats(struct sparx5_mall_entry
*entry
,
207 struct flow_stats
*fstats
)
209 struct sparx5_port
*port
= entry
->port
;
210 struct rtnl_link_stats64 new_stats
;
211 struct flow_stats
*old_stats
;
213 old_stats
= &entry
->port
->mirror_stats
;
214 sparx5_get_stats64(port
->ndev
, &new_stats
);
216 if (entry
->ingress
) {
217 flow_stats_update(fstats
,
218 new_stats
.rx_bytes
- old_stats
->bytes
,
219 new_stats
.rx_packets
- old_stats
->pkts
,
220 new_stats
.rx_dropped
- old_stats
->drops
,
222 FLOW_ACTION_HW_STATS_IMMEDIATE
);
224 old_stats
->bytes
= new_stats
.rx_bytes
;
225 old_stats
->pkts
= new_stats
.rx_packets
;
226 old_stats
->drops
= new_stats
.rx_dropped
;
227 old_stats
->lastused
= jiffies
;
229 flow_stats_update(fstats
,
230 new_stats
.tx_bytes
- old_stats
->bytes
,
231 new_stats
.tx_packets
- old_stats
->pkts
,
232 new_stats
.tx_dropped
- old_stats
->drops
,
234 FLOW_ACTION_HW_STATS_IMMEDIATE
);
236 old_stats
->bytes
= new_stats
.tx_bytes
;
237 old_stats
->pkts
= new_stats
.tx_packets
;
238 old_stats
->drops
= new_stats
.tx_dropped
;
239 old_stats
->lastused
= jiffies
;