1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 /* Statistics for Ocelot switch family 3 * 4 * Copyright (c) 2017 Microsemi Corporation 5 * Copyright 2022 NXP 6 */ 7 #include <linux/spinlock.h> 8 #include <linux/mutex.h> 9 #include <linux/workqueue.h> 10 #include "ocelot.h" 11 12 enum ocelot_stat { 13 OCELOT_STAT_RX_OCTETS, 14 OCELOT_STAT_RX_UNICAST, 15 OCELOT_STAT_RX_MULTICAST, 16 OCELOT_STAT_RX_BROADCAST, 17 OCELOT_STAT_RX_SHORTS, 18 OCELOT_STAT_RX_FRAGMENTS, 19 OCELOT_STAT_RX_JABBERS, 20 OCELOT_STAT_RX_CRC_ALIGN_ERRS, 21 OCELOT_STAT_RX_SYM_ERRS, 22 OCELOT_STAT_RX_64, 23 OCELOT_STAT_RX_65_127, 24 OCELOT_STAT_RX_128_255, 25 OCELOT_STAT_RX_256_511, 26 OCELOT_STAT_RX_512_1023, 27 OCELOT_STAT_RX_1024_1526, 28 OCELOT_STAT_RX_1527_MAX, 29 OCELOT_STAT_RX_PAUSE, 30 OCELOT_STAT_RX_CONTROL, 31 OCELOT_STAT_RX_LONGS, 32 OCELOT_STAT_RX_CLASSIFIED_DROPS, 33 OCELOT_STAT_RX_RED_PRIO_0, 34 OCELOT_STAT_RX_RED_PRIO_1, 35 OCELOT_STAT_RX_RED_PRIO_2, 36 OCELOT_STAT_RX_RED_PRIO_3, 37 OCELOT_STAT_RX_RED_PRIO_4, 38 OCELOT_STAT_RX_RED_PRIO_5, 39 OCELOT_STAT_RX_RED_PRIO_6, 40 OCELOT_STAT_RX_RED_PRIO_7, 41 OCELOT_STAT_RX_YELLOW_PRIO_0, 42 OCELOT_STAT_RX_YELLOW_PRIO_1, 43 OCELOT_STAT_RX_YELLOW_PRIO_2, 44 OCELOT_STAT_RX_YELLOW_PRIO_3, 45 OCELOT_STAT_RX_YELLOW_PRIO_4, 46 OCELOT_STAT_RX_YELLOW_PRIO_5, 47 OCELOT_STAT_RX_YELLOW_PRIO_6, 48 OCELOT_STAT_RX_YELLOW_PRIO_7, 49 OCELOT_STAT_RX_GREEN_PRIO_0, 50 OCELOT_STAT_RX_GREEN_PRIO_1, 51 OCELOT_STAT_RX_GREEN_PRIO_2, 52 OCELOT_STAT_RX_GREEN_PRIO_3, 53 OCELOT_STAT_RX_GREEN_PRIO_4, 54 OCELOT_STAT_RX_GREEN_PRIO_5, 55 OCELOT_STAT_RX_GREEN_PRIO_6, 56 OCELOT_STAT_RX_GREEN_PRIO_7, 57 OCELOT_STAT_TX_OCTETS, 58 OCELOT_STAT_TX_UNICAST, 59 OCELOT_STAT_TX_MULTICAST, 60 OCELOT_STAT_TX_BROADCAST, 61 OCELOT_STAT_TX_COLLISION, 62 OCELOT_STAT_TX_DROPS, 63 OCELOT_STAT_TX_PAUSE, 64 OCELOT_STAT_TX_64, 65 OCELOT_STAT_TX_65_127, 66 OCELOT_STAT_TX_128_255, 67 OCELOT_STAT_TX_256_511, 68 OCELOT_STAT_TX_512_1023, 69 OCELOT_STAT_TX_1024_1526, 70 OCELOT_STAT_TX_1527_MAX, 71 OCELOT_STAT_TX_YELLOW_PRIO_0, 72 OCELOT_STAT_TX_YELLOW_PRIO_1, 73 OCELOT_STAT_TX_YELLOW_PRIO_2, 74 OCELOT_STAT_TX_YELLOW_PRIO_3, 75 OCELOT_STAT_TX_YELLOW_PRIO_4, 76 OCELOT_STAT_TX_YELLOW_PRIO_5, 77 OCELOT_STAT_TX_YELLOW_PRIO_6, 78 OCELOT_STAT_TX_YELLOW_PRIO_7, 79 OCELOT_STAT_TX_GREEN_PRIO_0, 80 OCELOT_STAT_TX_GREEN_PRIO_1, 81 OCELOT_STAT_TX_GREEN_PRIO_2, 82 OCELOT_STAT_TX_GREEN_PRIO_3, 83 OCELOT_STAT_TX_GREEN_PRIO_4, 84 OCELOT_STAT_TX_GREEN_PRIO_5, 85 OCELOT_STAT_TX_GREEN_PRIO_6, 86 OCELOT_STAT_TX_GREEN_PRIO_7, 87 OCELOT_STAT_TX_AGED, 88 OCELOT_STAT_DROP_LOCAL, 89 OCELOT_STAT_DROP_TAIL, 90 OCELOT_STAT_DROP_YELLOW_PRIO_0, 91 OCELOT_STAT_DROP_YELLOW_PRIO_1, 92 OCELOT_STAT_DROP_YELLOW_PRIO_2, 93 OCELOT_STAT_DROP_YELLOW_PRIO_3, 94 OCELOT_STAT_DROP_YELLOW_PRIO_4, 95 OCELOT_STAT_DROP_YELLOW_PRIO_5, 96 OCELOT_STAT_DROP_YELLOW_PRIO_6, 97 OCELOT_STAT_DROP_YELLOW_PRIO_7, 98 OCELOT_STAT_DROP_GREEN_PRIO_0, 99 OCELOT_STAT_DROP_GREEN_PRIO_1, 100 OCELOT_STAT_DROP_GREEN_PRIO_2, 101 OCELOT_STAT_DROP_GREEN_PRIO_3, 102 OCELOT_STAT_DROP_GREEN_PRIO_4, 103 OCELOT_STAT_DROP_GREEN_PRIO_5, 104 OCELOT_STAT_DROP_GREEN_PRIO_6, 105 OCELOT_STAT_DROP_GREEN_PRIO_7, 106 OCELOT_NUM_STATS, 107 }; 108 109 struct ocelot_stat_layout { 110 u32 reg; 111 char name[ETH_GSTRING_LEN]; 112 }; 113 114 /* 32-bit counter checked for wraparound by ocelot_port_update_stats() 115 * and copied to ocelot->stats. 116 */ 117 #define OCELOT_STAT(kind) \ 118 [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind } 119 /* Same as above, except also exported to ethtool -S. Standard counters should 120 * only be exposed to more specific interfaces rather than by their string name. 121 */ 122 #define OCELOT_STAT_ETHTOOL(kind, ethtool_name) \ 123 [OCELOT_STAT_ ## kind] = { .reg = SYS_COUNT_ ## kind, .name = ethtool_name } 124 125 #define OCELOT_COMMON_STATS \ 126 OCELOT_STAT_ETHTOOL(RX_OCTETS, "rx_octets"), \ 127 OCELOT_STAT_ETHTOOL(RX_UNICAST, "rx_unicast"), \ 128 OCELOT_STAT_ETHTOOL(RX_MULTICAST, "rx_multicast"), \ 129 OCELOT_STAT_ETHTOOL(RX_BROADCAST, "rx_broadcast"), \ 130 OCELOT_STAT_ETHTOOL(RX_SHORTS, "rx_shorts"), \ 131 OCELOT_STAT_ETHTOOL(RX_FRAGMENTS, "rx_fragments"), \ 132 OCELOT_STAT_ETHTOOL(RX_JABBERS, "rx_jabbers"), \ 133 OCELOT_STAT_ETHTOOL(RX_CRC_ALIGN_ERRS, "rx_crc_align_errs"), \ 134 OCELOT_STAT_ETHTOOL(RX_SYM_ERRS, "rx_sym_errs"), \ 135 OCELOT_STAT_ETHTOOL(RX_64, "rx_frames_below_65_octets"), \ 136 OCELOT_STAT_ETHTOOL(RX_65_127, "rx_frames_65_to_127_octets"), \ 137 OCELOT_STAT_ETHTOOL(RX_128_255, "rx_frames_128_to_255_octets"), \ 138 OCELOT_STAT_ETHTOOL(RX_256_511, "rx_frames_256_to_511_octets"), \ 139 OCELOT_STAT_ETHTOOL(RX_512_1023, "rx_frames_512_to_1023_octets"), \ 140 OCELOT_STAT_ETHTOOL(RX_1024_1526, "rx_frames_1024_to_1526_octets"), \ 141 OCELOT_STAT_ETHTOOL(RX_1527_MAX, "rx_frames_over_1526_octets"), \ 142 OCELOT_STAT_ETHTOOL(RX_PAUSE, "rx_pause"), \ 143 OCELOT_STAT_ETHTOOL(RX_CONTROL, "rx_control"), \ 144 OCELOT_STAT_ETHTOOL(RX_LONGS, "rx_longs"), \ 145 OCELOT_STAT_ETHTOOL(RX_CLASSIFIED_DROPS, "rx_classified_drops"), \ 146 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_0, "rx_red_prio_0"), \ 147 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_1, "rx_red_prio_1"), \ 148 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_2, "rx_red_prio_2"), \ 149 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_3, "rx_red_prio_3"), \ 150 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_4, "rx_red_prio_4"), \ 151 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_5, "rx_red_prio_5"), \ 152 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_6, "rx_red_prio_6"), \ 153 OCELOT_STAT_ETHTOOL(RX_RED_PRIO_7, "rx_red_prio_7"), \ 154 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_0, "rx_yellow_prio_0"), \ 155 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_1, "rx_yellow_prio_1"), \ 156 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_2, "rx_yellow_prio_2"), \ 157 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_3, "rx_yellow_prio_3"), \ 158 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_4, "rx_yellow_prio_4"), \ 159 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_5, "rx_yellow_prio_5"), \ 160 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_6, "rx_yellow_prio_6"), \ 161 OCELOT_STAT_ETHTOOL(RX_YELLOW_PRIO_7, "rx_yellow_prio_7"), \ 162 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_0, "rx_green_prio_0"), \ 163 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_1, "rx_green_prio_1"), \ 164 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_2, "rx_green_prio_2"), \ 165 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_3, "rx_green_prio_3"), \ 166 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_4, "rx_green_prio_4"), \ 167 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_5, "rx_green_prio_5"), \ 168 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_6, "rx_green_prio_6"), \ 169 OCELOT_STAT_ETHTOOL(RX_GREEN_PRIO_7, "rx_green_prio_7"), \ 170 OCELOT_STAT_ETHTOOL(TX_OCTETS, "tx_octets"), \ 171 OCELOT_STAT_ETHTOOL(TX_UNICAST, "tx_unicast"), \ 172 OCELOT_STAT_ETHTOOL(TX_MULTICAST, "tx_multicast"), \ 173 OCELOT_STAT_ETHTOOL(TX_BROADCAST, "tx_broadcast"), \ 174 OCELOT_STAT_ETHTOOL(TX_COLLISION, "tx_collision"), \ 175 OCELOT_STAT_ETHTOOL(TX_DROPS, "tx_drops"), \ 176 OCELOT_STAT_ETHTOOL(TX_PAUSE, "tx_pause"), \ 177 OCELOT_STAT_ETHTOOL(TX_64, "tx_frames_below_65_octets"), \ 178 OCELOT_STAT_ETHTOOL(TX_65_127, "tx_frames_65_to_127_octets"), \ 179 OCELOT_STAT_ETHTOOL(TX_128_255, "tx_frames_128_255_octets"), \ 180 OCELOT_STAT_ETHTOOL(TX_256_511, "tx_frames_256_511_octets"), \ 181 OCELOT_STAT_ETHTOOL(TX_512_1023, "tx_frames_512_1023_octets"), \ 182 OCELOT_STAT_ETHTOOL(TX_1024_1526, "tx_frames_1024_1526_octets"), \ 183 OCELOT_STAT_ETHTOOL(TX_1527_MAX, "tx_frames_over_1526_octets"), \ 184 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_0, "tx_yellow_prio_0"), \ 185 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_1, "tx_yellow_prio_1"), \ 186 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_2, "tx_yellow_prio_2"), \ 187 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_3, "tx_yellow_prio_3"), \ 188 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_4, "tx_yellow_prio_4"), \ 189 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_5, "tx_yellow_prio_5"), \ 190 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_6, "tx_yellow_prio_6"), \ 191 OCELOT_STAT_ETHTOOL(TX_YELLOW_PRIO_7, "tx_yellow_prio_7"), \ 192 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_0, "tx_green_prio_0"), \ 193 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_1, "tx_green_prio_1"), \ 194 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_2, "tx_green_prio_2"), \ 195 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_3, "tx_green_prio_3"), \ 196 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_4, "tx_green_prio_4"), \ 197 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_5, "tx_green_prio_5"), \ 198 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_6, "tx_green_prio_6"), \ 199 OCELOT_STAT_ETHTOOL(TX_GREEN_PRIO_7, "tx_green_prio_7"), \ 200 OCELOT_STAT_ETHTOOL(TX_AGED, "tx_aged"), \ 201 OCELOT_STAT_ETHTOOL(DROP_LOCAL, "drop_local"), \ 202 OCELOT_STAT_ETHTOOL(DROP_TAIL, "drop_tail"), \ 203 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_0, "drop_yellow_prio_0"), \ 204 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_1, "drop_yellow_prio_1"), \ 205 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_2, "drop_yellow_prio_2"), \ 206 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_3, "drop_yellow_prio_3"), \ 207 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_4, "drop_yellow_prio_4"), \ 208 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_5, "drop_yellow_prio_5"), \ 209 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_6, "drop_yellow_prio_6"), \ 210 OCELOT_STAT_ETHTOOL(DROP_YELLOW_PRIO_7, "drop_yellow_prio_7"), \ 211 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_0, "drop_green_prio_0"), \ 212 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_1, "drop_green_prio_1"), \ 213 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_2, "drop_green_prio_2"), \ 214 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_3, "drop_green_prio_3"), \ 215 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_4, "drop_green_prio_4"), \ 216 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_5, "drop_green_prio_5"), \ 217 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_6, "drop_green_prio_6"), \ 218 OCELOT_STAT_ETHTOOL(DROP_GREEN_PRIO_7, "drop_green_prio_7") 219 220 struct ocelot_stats_region { 221 struct list_head node; 222 u32 base; 223 int count; 224 u32 *buf; 225 }; 226 227 static const struct ocelot_stat_layout ocelot_stats_layout[OCELOT_NUM_STATS] = { 228 OCELOT_COMMON_STATS, 229 }; 230 231 /* Read the counters from hardware and keep them in region->buf. 232 * Caller must hold &ocelot->stat_view_lock. 233 */ 234 static int ocelot_port_update_stats(struct ocelot *ocelot, int port) 235 { 236 struct ocelot_stats_region *region; 237 int err; 238 239 /* Configure the port to read the stats from */ 240 ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); 241 242 list_for_each_entry(region, &ocelot->stats_regions, node) { 243 err = ocelot_bulk_read(ocelot, region->base, region->buf, 244 region->count); 245 if (err) 246 return err; 247 } 248 249 return 0; 250 } 251 252 /* Transfer the counters from region->buf to ocelot->stats. 253 * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock. 254 */ 255 static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port) 256 { 257 unsigned int idx = port * OCELOT_NUM_STATS; 258 struct ocelot_stats_region *region; 259 int j; 260 261 list_for_each_entry(region, &ocelot->stats_regions, node) { 262 for (j = 0; j < region->count; j++) { 263 u64 *stat = &ocelot->stats[idx + j]; 264 u64 val = region->buf[j]; 265 266 if (val < (*stat & U32_MAX)) 267 *stat += (u64)1 << 32; 268 269 *stat = (*stat & ~(u64)U32_MAX) + val; 270 } 271 272 idx += region->count; 273 } 274 } 275 276 static void ocelot_check_stats_work(struct work_struct *work) 277 { 278 struct delayed_work *del_work = to_delayed_work(work); 279 struct ocelot *ocelot = container_of(del_work, struct ocelot, 280 stats_work); 281 int port, err; 282 283 mutex_lock(&ocelot->stat_view_lock); 284 285 for (port = 0; port < ocelot->num_phys_ports; port++) { 286 err = ocelot_port_update_stats(ocelot, port); 287 if (err) 288 break; 289 290 spin_lock(&ocelot->stats_lock); 291 ocelot_port_transfer_stats(ocelot, port); 292 spin_unlock(&ocelot->stats_lock); 293 } 294 295 if (!err && ocelot->ops->update_stats) 296 ocelot->ops->update_stats(ocelot); 297 298 mutex_unlock(&ocelot->stat_view_lock); 299 300 if (err) 301 dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); 302 303 queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 304 OCELOT_STATS_CHECK_DELAY); 305 } 306 307 void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) 308 { 309 int i; 310 311 if (sset != ETH_SS_STATS) 312 return; 313 314 for (i = 0; i < OCELOT_NUM_STATS; i++) { 315 if (ocelot_stats_layout[i].name[0] == '\0') 316 continue; 317 318 memcpy(data + i * ETH_GSTRING_LEN, ocelot_stats_layout[i].name, 319 ETH_GSTRING_LEN); 320 } 321 } 322 EXPORT_SYMBOL(ocelot_get_strings); 323 324 /* Update ocelot->stats for the given port and run the given callback */ 325 static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv, 326 void (*cb)(struct ocelot *ocelot, int port, 327 void *priv)) 328 { 329 int err; 330 331 mutex_lock(&ocelot->stat_view_lock); 332 333 err = ocelot_port_update_stats(ocelot, port); 334 if (err) { 335 dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n", 336 port, ERR_PTR(err)); 337 goto out_unlock; 338 } 339 340 spin_lock(&ocelot->stats_lock); 341 342 ocelot_port_transfer_stats(ocelot, port); 343 cb(ocelot, port, priv); 344 345 spin_unlock(&ocelot->stats_lock); 346 347 out_unlock: 348 mutex_unlock(&ocelot->stat_view_lock); 349 } 350 351 int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) 352 { 353 int i, num_stats = 0; 354 355 if (sset != ETH_SS_STATS) 356 return -EOPNOTSUPP; 357 358 for (i = 0; i < OCELOT_NUM_STATS; i++) 359 if (ocelot_stats_layout[i].name[0] != '\0') 360 num_stats++; 361 362 return num_stats; 363 } 364 EXPORT_SYMBOL(ocelot_get_sset_count); 365 366 static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port, 367 void *priv) 368 { 369 u64 *data = priv; 370 int i; 371 372 /* Copy all supported counters */ 373 for (i = 0; i < OCELOT_NUM_STATS; i++) { 374 int index = port * OCELOT_NUM_STATS + i; 375 376 if (ocelot_stats_layout[i].name[0] == '\0') 377 continue; 378 379 *data++ = ocelot->stats[index]; 380 } 381 } 382 383 void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) 384 { 385 ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb); 386 } 387 EXPORT_SYMBOL(ocelot_get_ethtool_stats); 388 389 static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv) 390 { 391 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 392 struct ethtool_pause_stats *pause_stats = priv; 393 394 pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE]; 395 pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE]; 396 } 397 398 void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port, 399 struct ethtool_pause_stats *pause_stats) 400 { 401 ocelot_port_stats_run(ocelot, port, pause_stats, 402 ocelot_port_pause_stats_cb); 403 } 404 EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats); 405 406 static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = { 407 { 64, 64 }, 408 { 65, 127 }, 409 { 128, 255 }, 410 { 256, 511 }, 411 { 512, 1023 }, 412 { 1024, 1526 }, 413 { 1527, 65535 }, 414 {}, 415 }; 416 417 static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv) 418 { 419 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 420 struct ethtool_rmon_stats *rmon_stats = priv; 421 422 rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS]; 423 rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS]; 424 rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS]; 425 rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS]; 426 427 rmon_stats->hist[0] = s[OCELOT_STAT_RX_64]; 428 rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127]; 429 rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255]; 430 rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511]; 431 rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023]; 432 rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526]; 433 rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX]; 434 435 rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64]; 436 rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127]; 437 rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255]; 438 rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255]; 439 rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511]; 440 rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023]; 441 rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526]; 442 } 443 444 void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port, 445 struct ethtool_rmon_stats *rmon_stats, 446 const struct ethtool_rmon_hist_range **ranges) 447 { 448 *ranges = ocelot_rmon_ranges; 449 450 ocelot_port_stats_run(ocelot, port, rmon_stats, 451 ocelot_port_rmon_stats_cb); 452 } 453 EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats); 454 455 static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv) 456 { 457 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 458 struct ethtool_eth_ctrl_stats *ctrl_stats = priv; 459 460 ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL]; 461 } 462 463 void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port, 464 struct ethtool_eth_ctrl_stats *ctrl_stats) 465 { 466 ocelot_port_stats_run(ocelot, port, ctrl_stats, 467 ocelot_port_ctrl_stats_cb); 468 } 469 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats); 470 471 static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv) 472 { 473 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 474 struct ethtool_eth_mac_stats *mac_stats = priv; 475 476 mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS]; 477 mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] + 478 s[OCELOT_STAT_TX_65_127] + 479 s[OCELOT_STAT_TX_128_255] + 480 s[OCELOT_STAT_TX_256_511] + 481 s[OCELOT_STAT_TX_512_1023] + 482 s[OCELOT_STAT_TX_1024_1526] + 483 s[OCELOT_STAT_TX_1527_MAX]; 484 mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS]; 485 mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] + 486 s[OCELOT_STAT_RX_GREEN_PRIO_1] + 487 s[OCELOT_STAT_RX_GREEN_PRIO_2] + 488 s[OCELOT_STAT_RX_GREEN_PRIO_3] + 489 s[OCELOT_STAT_RX_GREEN_PRIO_4] + 490 s[OCELOT_STAT_RX_GREEN_PRIO_5] + 491 s[OCELOT_STAT_RX_GREEN_PRIO_6] + 492 s[OCELOT_STAT_RX_GREEN_PRIO_7] + 493 s[OCELOT_STAT_RX_YELLOW_PRIO_0] + 494 s[OCELOT_STAT_RX_YELLOW_PRIO_1] + 495 s[OCELOT_STAT_RX_YELLOW_PRIO_2] + 496 s[OCELOT_STAT_RX_YELLOW_PRIO_3] + 497 s[OCELOT_STAT_RX_YELLOW_PRIO_4] + 498 s[OCELOT_STAT_RX_YELLOW_PRIO_5] + 499 s[OCELOT_STAT_RX_YELLOW_PRIO_6] + 500 s[OCELOT_STAT_RX_YELLOW_PRIO_7]; 501 mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST]; 502 mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST]; 503 mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST]; 504 mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST]; 505 mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS]; 506 /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not 507 * counted individually. 508 */ 509 mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; 510 mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; 511 } 512 513 void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port, 514 struct ethtool_eth_mac_stats *mac_stats) 515 { 516 ocelot_port_stats_run(ocelot, port, mac_stats, 517 ocelot_port_mac_stats_cb); 518 } 519 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats); 520 521 static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv) 522 { 523 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 524 struct ethtool_eth_phy_stats *phy_stats = priv; 525 526 phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS]; 527 } 528 529 void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port, 530 struct ethtool_eth_phy_stats *phy_stats) 531 { 532 ocelot_port_stats_run(ocelot, port, phy_stats, 533 ocelot_port_phy_stats_cb); 534 } 535 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats); 536 537 void ocelot_port_get_stats64(struct ocelot *ocelot, int port, 538 struct rtnl_link_stats64 *stats) 539 { 540 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 541 542 spin_lock(&ocelot->stats_lock); 543 544 /* Get Rx stats */ 545 stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; 546 stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + 547 s[OCELOT_STAT_RX_FRAGMENTS] + 548 s[OCELOT_STAT_RX_JABBERS] + 549 s[OCELOT_STAT_RX_LONGS] + 550 s[OCELOT_STAT_RX_64] + 551 s[OCELOT_STAT_RX_65_127] + 552 s[OCELOT_STAT_RX_128_255] + 553 s[OCELOT_STAT_RX_256_511] + 554 s[OCELOT_STAT_RX_512_1023] + 555 s[OCELOT_STAT_RX_1024_1526] + 556 s[OCELOT_STAT_RX_1527_MAX]; 557 stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; 558 stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL]; 559 stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] + 560 s[OCELOT_STAT_RX_RED_PRIO_1] + 561 s[OCELOT_STAT_RX_RED_PRIO_2] + 562 s[OCELOT_STAT_RX_RED_PRIO_3] + 563 s[OCELOT_STAT_RX_RED_PRIO_4] + 564 s[OCELOT_STAT_RX_RED_PRIO_5] + 565 s[OCELOT_STAT_RX_RED_PRIO_6] + 566 s[OCELOT_STAT_RX_RED_PRIO_7] + 567 s[OCELOT_STAT_DROP_LOCAL] + 568 s[OCELOT_STAT_DROP_YELLOW_PRIO_0] + 569 s[OCELOT_STAT_DROP_YELLOW_PRIO_1] + 570 s[OCELOT_STAT_DROP_YELLOW_PRIO_2] + 571 s[OCELOT_STAT_DROP_YELLOW_PRIO_3] + 572 s[OCELOT_STAT_DROP_YELLOW_PRIO_4] + 573 s[OCELOT_STAT_DROP_YELLOW_PRIO_5] + 574 s[OCELOT_STAT_DROP_YELLOW_PRIO_6] + 575 s[OCELOT_STAT_DROP_YELLOW_PRIO_7] + 576 s[OCELOT_STAT_DROP_GREEN_PRIO_0] + 577 s[OCELOT_STAT_DROP_GREEN_PRIO_1] + 578 s[OCELOT_STAT_DROP_GREEN_PRIO_2] + 579 s[OCELOT_STAT_DROP_GREEN_PRIO_3] + 580 s[OCELOT_STAT_DROP_GREEN_PRIO_4] + 581 s[OCELOT_STAT_DROP_GREEN_PRIO_5] + 582 s[OCELOT_STAT_DROP_GREEN_PRIO_6] + 583 s[OCELOT_STAT_DROP_GREEN_PRIO_7]; 584 585 /* Get Tx stats */ 586 stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; 587 stats->tx_packets = s[OCELOT_STAT_TX_64] + 588 s[OCELOT_STAT_TX_65_127] + 589 s[OCELOT_STAT_TX_128_255] + 590 s[OCELOT_STAT_TX_256_511] + 591 s[OCELOT_STAT_TX_512_1023] + 592 s[OCELOT_STAT_TX_1024_1526] + 593 s[OCELOT_STAT_TX_1527_MAX]; 594 stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + 595 s[OCELOT_STAT_TX_AGED]; 596 stats->collisions = s[OCELOT_STAT_TX_COLLISION]; 597 598 spin_unlock(&ocelot->stats_lock); 599 } 600 EXPORT_SYMBOL(ocelot_port_get_stats64); 601 602 static int ocelot_prepare_stats_regions(struct ocelot *ocelot) 603 { 604 struct ocelot_stats_region *region = NULL; 605 unsigned int last = 0; 606 int i; 607 608 INIT_LIST_HEAD(&ocelot->stats_regions); 609 610 for (i = 0; i < OCELOT_NUM_STATS; i++) { 611 if (!ocelot_stats_layout[i].reg) 612 continue; 613 614 if (region && ocelot_stats_layout[i].reg == last + 4) { 615 region->count++; 616 } else { 617 region = devm_kzalloc(ocelot->dev, sizeof(*region), 618 GFP_KERNEL); 619 if (!region) 620 return -ENOMEM; 621 622 /* enum ocelot_stat must be kept sorted in the same 623 * order as ocelot_stats_layout[i].reg in order to have 624 * efficient bulking 625 */ 626 WARN_ON(last >= ocelot_stats_layout[i].reg); 627 628 region->base = ocelot_stats_layout[i].reg; 629 region->count = 1; 630 list_add_tail(®ion->node, &ocelot->stats_regions); 631 } 632 633 last = ocelot_stats_layout[i].reg; 634 } 635 636 list_for_each_entry(region, &ocelot->stats_regions, node) { 637 region->buf = devm_kcalloc(ocelot->dev, region->count, 638 sizeof(*region->buf), GFP_KERNEL); 639 if (!region->buf) 640 return -ENOMEM; 641 } 642 643 return 0; 644 } 645 646 int ocelot_stats_init(struct ocelot *ocelot) 647 { 648 char queue_name[32]; 649 int ret; 650 651 ocelot->stats = devm_kcalloc(ocelot->dev, 652 ocelot->num_phys_ports * OCELOT_NUM_STATS, 653 sizeof(u64), GFP_KERNEL); 654 if (!ocelot->stats) 655 return -ENOMEM; 656 657 snprintf(queue_name, sizeof(queue_name), "%s-stats", 658 dev_name(ocelot->dev)); 659 ocelot->stats_queue = create_singlethread_workqueue(queue_name); 660 if (!ocelot->stats_queue) 661 return -ENOMEM; 662 663 spin_lock_init(&ocelot->stats_lock); 664 mutex_init(&ocelot->stat_view_lock); 665 666 ret = ocelot_prepare_stats_regions(ocelot); 667 if (ret) { 668 destroy_workqueue(ocelot->stats_queue); 669 return ret; 670 } 671 672 INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); 673 queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 674 OCELOT_STATS_CHECK_DELAY); 675 676 return 0; 677 } 678 679 void ocelot_stats_deinit(struct ocelot *ocelot) 680 { 681 cancel_delayed_work(&ocelot->stats_work); 682 destroy_workqueue(ocelot->stats_queue); 683 } 684 685