xref: /linux/drivers/net/ethernet/microchip/sparx5/sparx5_mirror.c (revision 150b567e0d572342ef08bace7ee7aff80fd75327)
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
3  *
4  * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
5  */
6 
7 #include "sparx5_main.h"
8 #include "sparx5_main_regs.h"
9 #include "sparx5_tc.h"
10 
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_QFWD_MP_OFFSET 9 /* Mirror port offset in the QFWD register */
16 
17 /* Convert from bool ingress/egress to mirror direction */
sparx5_mirror_to_dir(bool ingress)18 static u32 sparx5_mirror_to_dir(bool ingress)
19 {
20 	return ingress ? SPX5_MIRROR_INGRESS : SPX5_MIRROR_EGRESS;
21 }
22 
23 /* Get ports belonging to this mirror */
sparx5_mirror_port_get(struct sparx5 * sparx5,u32 idx)24 static u64 sparx5_mirror_port_get(struct sparx5 *sparx5, u32 idx)
25 {
26 	u64 val;
27 
28 	val = spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG(idx));
29 
30 	if (is_sparx5(sparx5))
31 		val |= (u64)spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG1(idx)) << 32;
32 
33 	return val;
34 }
35 
36 /* Add port to mirror (only front ports) */
sparx5_mirror_port_add(struct sparx5 * sparx5,u32 idx,u32 portno)37 static void sparx5_mirror_port_add(struct sparx5 *sparx5, u32 idx, u32 portno)
38 {
39 	u64 reg = portno;
40 	u32 val;
41 
42 	val = BIT(do_div(reg, 32));
43 
44 	if (reg == 0)
45 		return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));
46 	else
47 		return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG1(idx));
48 }
49 
50 /* Delete port from mirror (only front ports) */
sparx5_mirror_port_del(struct sparx5 * sparx5,u32 idx,u32 portno)51 static void sparx5_mirror_port_del(struct sparx5 *sparx5, u32 idx, u32 portno)
52 {
53 	u64 reg = portno;
54 	u32 val;
55 
56 	val = BIT(do_div(reg, 32));
57 
58 	if (reg == 0)
59 		return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));
60 	else
61 		return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG1(idx));
62 }
63 
64 /* Check if mirror contains port */
sparx5_mirror_contains(struct sparx5 * sparx5,u32 idx,u32 portno)65 static bool sparx5_mirror_contains(struct sparx5 *sparx5, u32 idx, u32 portno)
66 {
67 	return (sparx5_mirror_port_get(sparx5, idx) & BIT_ULL(portno)) != 0;
68 }
69 
70 /* Check if mirror is empty */
sparx5_mirror_is_empty(struct sparx5 * sparx5,u32 idx)71 static bool sparx5_mirror_is_empty(struct sparx5 *sparx5, u32 idx)
72 {
73 	return sparx5_mirror_port_get(sparx5, idx) == 0;
74 }
75 
76 /* Get direction of mirror */
sparx5_mirror_dir_get(struct sparx5 * sparx5,u32 idx)77 static u32 sparx5_mirror_dir_get(struct sparx5 *sparx5, u32 idx)
78 {
79 	u32 val = spx5_rd(sparx5, ANA_AC_PROBE_CFG(idx));
80 
81 	return ANA_AC_PROBE_CFG_PROBE_DIRECTION_GET(val);
82 }
83 
84 /* Set direction of mirror */
sparx5_mirror_dir_set(struct sparx5 * sparx5,u32 idx,u32 dir)85 static void sparx5_mirror_dir_set(struct sparx5 *sparx5, u32 idx, u32 dir)
86 {
87 	spx5_rmw(ANA_AC_PROBE_CFG_PROBE_DIRECTION_SET(dir),
88 		 ANA_AC_PROBE_CFG_PROBE_DIRECTION, sparx5,
89 		 ANA_AC_PROBE_CFG(idx));
90 }
91 
92 /* Set the monitor port for this mirror */
sparx5_mirror_monitor_set(struct sparx5 * sparx5,u32 idx,u32 portno)93 static void sparx5_mirror_monitor_set(struct sparx5 *sparx5, u32 idx,
94 				      u32 portno)
95 {
96 	spx5_rmw(QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_SET(portno),
97 		 QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL, sparx5,
98 		 QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET));
99 }
100 
101 /* Get the monitor port of this mirror */
sparx5_mirror_monitor_get(struct sparx5 * sparx5,u32 idx)102 static u32 sparx5_mirror_monitor_get(struct sparx5 *sparx5, u32 idx)
103 {
104 	u32 val = spx5_rd(sparx5,
105 			  QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET));
106 
107 	return QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_GET(val);
108 }
109 
110 /* Check if port is the monitor port of this mirror */
sparx5_mirror_has_monitor(struct sparx5 * sparx5,u32 idx,u32 portno)111 static bool sparx5_mirror_has_monitor(struct sparx5 *sparx5, u32 idx,
112 				      u32 portno)
113 {
114 	return sparx5_mirror_monitor_get(sparx5, idx) == portno;
115 }
116 
117 /* Get a suitable mirror for this port */
sparx5_mirror_get(struct sparx5_port * sport,struct sparx5_port * mport,u32 dir,u32 * idx)118 static int sparx5_mirror_get(struct sparx5_port *sport,
119 			     struct sparx5_port *mport, u32 dir, u32 *idx)
120 {
121 	struct sparx5 *sparx5 = sport->sparx5;
122 	u32 i;
123 
124 	/* Check if this port is already used as a monitor port */
125 	for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++)
126 		if (sparx5_mirror_has_monitor(sparx5, i, sport->portno))
127 			return -EINVAL;
128 
129 	/* Check if existing mirror can be reused
130 	 * (same direction and monitor port).
131 	 */
132 	for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {
133 		if (sparx5_mirror_dir_get(sparx5, i) == dir &&
134 		    sparx5_mirror_has_monitor(sparx5, i, mport->portno)) {
135 			*idx = i;
136 			return 0;
137 		}
138 	}
139 
140 	/* Return free mirror */
141 	for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {
142 		if (sparx5_mirror_is_empty(sparx5, i)) {
143 			*idx = i;
144 			return 0;
145 		}
146 	}
147 
148 	return -ENOENT;
149 }
150 
sparx5_mirror_add(struct sparx5_mall_entry * entry)151 int sparx5_mirror_add(struct sparx5_mall_entry *entry)
152 {
153 	u32 mirror_idx, dir = sparx5_mirror_to_dir(entry->ingress);
154 	struct sparx5_port *sport, *mport;
155 	struct sparx5 *sparx5;
156 	int err;
157 
158 	/* Source port */
159 	sport = entry->port;
160 	/* monitor port */
161 	mport = entry->mirror.port;
162 	sparx5 = sport->sparx5;
163 
164 	if (sport->portno == mport->portno)
165 		return -EINVAL;
166 
167 	err = sparx5_mirror_get(sport, mport, dir, &mirror_idx);
168 	if (err)
169 		return err;
170 
171 	if (sparx5_mirror_contains(sparx5, mirror_idx, sport->portno))
172 		return -EEXIST;
173 
174 	/* Add port to mirror */
175 	sparx5_mirror_port_add(sparx5, mirror_idx, sport->portno);
176 
177 	/* Set direction of mirror */
178 	sparx5_mirror_dir_set(sparx5, mirror_idx, dir);
179 
180 	/* Set monitor port for mirror */
181 	sparx5_mirror_monitor_set(sparx5, mirror_idx, mport->portno);
182 
183 	entry->mirror.idx = mirror_idx;
184 
185 	return 0;
186 }
187 
sparx5_mirror_del(struct sparx5_mall_entry * entry)188 void sparx5_mirror_del(struct sparx5_mall_entry *entry)
189 {
190 	struct sparx5_port *port = entry->port;
191 	struct sparx5 *sparx5 = port->sparx5;
192 	u32 mirror_idx = entry->mirror.idx;
193 
194 	sparx5_mirror_port_del(sparx5, mirror_idx, port->portno);
195 	if (!sparx5_mirror_is_empty(sparx5, mirror_idx))
196 		return;
197 
198 	sparx5_mirror_dir_set(sparx5, mirror_idx, SPX5_MIRROR_DISABLED);
199 
200 	sparx5_mirror_monitor_set(sparx5,
201 				  mirror_idx,
202 				  sparx5->data->consts->n_ports);
203 }
204 
sparx5_mirror_stats(struct sparx5_mall_entry * entry,struct flow_stats * fstats)205 void sparx5_mirror_stats(struct sparx5_mall_entry *entry,
206 			 struct flow_stats *fstats)
207 {
208 	struct sparx5_port *port = entry->port;
209 	struct rtnl_link_stats64 new_stats;
210 	struct flow_stats *old_stats;
211 
212 	old_stats = &entry->port->mirror_stats;
213 	sparx5_get_stats64(port->ndev, &new_stats);
214 
215 	if (entry->ingress) {
216 		flow_stats_update(fstats,
217 				  new_stats.rx_bytes - old_stats->bytes,
218 				  new_stats.rx_packets - old_stats->pkts,
219 				  new_stats.rx_dropped - old_stats->drops,
220 				  old_stats->lastused,
221 				  FLOW_ACTION_HW_STATS_IMMEDIATE);
222 
223 		old_stats->bytes = new_stats.rx_bytes;
224 		old_stats->pkts = new_stats.rx_packets;
225 		old_stats->drops = new_stats.rx_dropped;
226 		old_stats->lastused = jiffies;
227 	} else {
228 		flow_stats_update(fstats,
229 				  new_stats.tx_bytes - old_stats->bytes,
230 				  new_stats.tx_packets - old_stats->pkts,
231 				  new_stats.tx_dropped - old_stats->drops,
232 				  old_stats->lastused,
233 				  FLOW_ACTION_HW_STATS_IMMEDIATE);
234 
235 		old_stats->bytes = new_stats.tx_bytes;
236 		old_stats->pkts = new_stats.tx_packets;
237 		old_stats->drops = new_stats.tx_dropped;
238 		old_stats->lastused = jiffies;
239 	}
240 }
241