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 */ 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 */ 25 static u64 sparx5_mirror_port_get(struct sparx5 *sparx5, u32 idx) 26 { 27 u64 val; 28 29 val = spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG(idx)); 30 31 if (is_sparx5(sparx5)) 32 val |= (u64)spx5_rd(sparx5, ANA_AC_PROBE_PORT_CFG1(idx)) << 32; 33 34 return val; 35 } 36 37 /* Add port to mirror (only front ports) */ 38 static void sparx5_mirror_port_add(struct sparx5 *sparx5, u32 idx, u32 portno) 39 { 40 u64 reg = portno; 41 u32 val; 42 43 val = BIT(do_div(reg, 32)); 44 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)); 49 } 50 51 /* Delete port from mirror (only front ports) */ 52 static void sparx5_mirror_port_del(struct sparx5 *sparx5, u32 idx, u32 portno) 53 { 54 u64 reg = portno; 55 u32 val; 56 57 val = BIT(do_div(reg, 32)); 58 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)); 63 } 64 65 /* Check if mirror contains port */ 66 static bool sparx5_mirror_contains(struct sparx5 *sparx5, u32 idx, u32 portno) 67 { 68 return (sparx5_mirror_port_get(sparx5, idx) & BIT_ULL(portno)) != 0; 69 } 70 71 /* Check if mirror is empty */ 72 static bool sparx5_mirror_is_empty(struct sparx5 *sparx5, u32 idx) 73 { 74 return sparx5_mirror_port_get(sparx5, idx) == 0; 75 } 76 77 /* Get direction of mirror */ 78 static u32 sparx5_mirror_dir_get(struct sparx5 *sparx5, u32 idx) 79 { 80 u32 val = spx5_rd(sparx5, ANA_AC_PROBE_CFG(idx)); 81 82 return ANA_AC_PROBE_CFG_PROBE_DIRECTION_GET(val); 83 } 84 85 /* Set direction of mirror */ 86 static void sparx5_mirror_dir_set(struct sparx5 *sparx5, u32 idx, u32 dir) 87 { 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)); 91 } 92 93 /* Set the monitor port for this mirror */ 94 static void sparx5_mirror_monitor_set(struct sparx5 *sparx5, u32 idx, 95 u32 portno) 96 { 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)); 100 } 101 102 /* Get the monitor port of this mirror */ 103 static u32 sparx5_mirror_monitor_get(struct sparx5 *sparx5, u32 idx) 104 { 105 u32 val = spx5_rd(sparx5, 106 QFWD_FRAME_COPY_CFG(idx + SPX5_QFWD_MP_OFFSET)); 107 108 return QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL_GET(val); 109 } 110 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) 114 { 115 return sparx5_mirror_monitor_get(sparx5, idx) == portno; 116 } 117 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) 121 { 122 struct sparx5 *sparx5 = sport->sparx5; 123 u32 i; 124 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; 129 130 /* Check if existing mirror can be reused 131 * (same direction and monitor port). 132 */ 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; 138 } 139 } 140 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; 146 } 147 } 148 149 return -ENOENT; 150 } 151 152 int sparx5_mirror_add(struct sparx5_mall_entry *entry) 153 { 154 u32 mirror_idx, dir = sparx5_mirror_to_dir(entry->ingress); 155 struct sparx5_port *sport, *mport; 156 struct sparx5 *sparx5; 157 int err; 158 159 /* Source port */ 160 sport = entry->port; 161 /* monitor port */ 162 mport = entry->mirror.port; 163 sparx5 = sport->sparx5; 164 165 if (sport->portno == mport->portno) 166 return -EINVAL; 167 168 err = sparx5_mirror_get(sport, mport, dir, &mirror_idx); 169 if (err) 170 return err; 171 172 if (sparx5_mirror_contains(sparx5, mirror_idx, sport->portno)) 173 return -EEXIST; 174 175 /* Add port to mirror */ 176 sparx5_mirror_port_add(sparx5, mirror_idx, sport->portno); 177 178 /* Set direction of mirror */ 179 sparx5_mirror_dir_set(sparx5, mirror_idx, dir); 180 181 /* Set monitor port for mirror */ 182 sparx5_mirror_monitor_set(sparx5, mirror_idx, mport->portno); 183 184 entry->mirror.idx = mirror_idx; 185 186 return 0; 187 } 188 189 void sparx5_mirror_del(struct sparx5_mall_entry *entry) 190 { 191 struct sparx5_port *port = entry->port; 192 struct sparx5 *sparx5 = port->sparx5; 193 u32 mirror_idx = entry->mirror.idx; 194 195 sparx5_mirror_port_del(sparx5, mirror_idx, port->portno); 196 if (!sparx5_mirror_is_empty(sparx5, mirror_idx)) 197 return; 198 199 sparx5_mirror_dir_set(sparx5, mirror_idx, SPX5_MIRROR_DISABLED); 200 201 sparx5_mirror_monitor_set(sparx5, 202 mirror_idx, 203 SPX5_MIRROR_MONITOR_PORT_DEFAULT); 204 } 205 206 void sparx5_mirror_stats(struct sparx5_mall_entry *entry, 207 struct flow_stats *fstats) 208 { 209 struct sparx5_port *port = entry->port; 210 struct rtnl_link_stats64 new_stats; 211 struct flow_stats *old_stats; 212 213 old_stats = &entry->port->mirror_stats; 214 sparx5_get_stats64(port->ndev, &new_stats); 215 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); 223 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); 235 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; 240 } 241 } 242