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 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) */ 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) */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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