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