drm/vkms: Add support for ABGR8888 pixel format
[drm/drm-misc.git] / drivers / net / ethernet / microchip / sparx5 / sparx5_mirror.c
blob9806729e9c6243c86699d80a6560abc83dcd447b
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
5 */
7 #include "sparx5_main.h"
8 #include "sparx5_main_regs.h"
9 #include "sparx5_tc.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)
27 u64 val;
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;
34 return val;
37 /* Add port to mirror (only front ports) */
38 static void sparx5_mirror_port_add(struct sparx5 *sparx5, u32 idx, u32 portno)
40 u64 reg = portno;
41 u32 val;
43 val = BIT(do_div(reg, 32));
45 if (reg == 0)
46 return spx5_rmw(val, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));
47 else
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)
54 u64 reg = portno;
55 u32 val;
57 val = BIT(do_div(reg, 32));
59 if (reg == 0)
60 return spx5_rmw(0, val, sparx5, ANA_AC_PROBE_PORT_CFG(idx));
61 else
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,
95 u32 portno)
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,
113 u32 portno)
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;
123 u32 i;
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))
128 return -EINVAL;
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)) {
136 *idx = i;
137 return 0;
141 /* Return free mirror */
142 for (i = 0; i < SPX5_MIRROR_PROBE_MAX; i++) {
143 if (sparx5_mirror_is_empty(sparx5, i)) {
144 *idx = i;
145 return 0;
149 return -ENOENT;
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;
157 int err;
159 /* Source port */
160 sport = entry->port;
161 /* monitor port */
162 mport = entry->mirror.port;
163 sparx5 = sport->sparx5;
165 if (sport->portno == mport->portno)
166 return -EINVAL;
168 err = sparx5_mirror_get(sport, mport, dir, &mirror_idx);
169 if (err)
170 return err;
172 if (sparx5_mirror_contains(sparx5, mirror_idx, sport->portno))
173 return -EEXIST;
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;
186 return 0;
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))
197 return;
199 sparx5_mirror_dir_set(sparx5, mirror_idx, SPX5_MIRROR_DISABLED);
201 sparx5_mirror_monitor_set(sparx5,
202 mirror_idx,
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,
221 old_stats->lastused,
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;
228 } else {
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,
233 old_stats->lastused,
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;