Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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 */
 18static 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 */
 24static 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) */
 37static 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) */
 51static 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 */
 65static 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 */
 71static 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 */
 77static 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 */
 85static 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 */
 93static 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 */
102static 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 */
111static 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 */
118static 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
151int 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
188void 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
205void 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}