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 /* Read the counters from hardware and keep them in region->buf. 13 * Caller must hold &ocelot->stat_view_lock. 14 */ 15 static int ocelot_port_update_stats(struct ocelot *ocelot, int port) 16 { 17 struct ocelot_stats_region *region; 18 int err; 19 20 /* Configure the port to read the stats from */ 21 ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); 22 23 list_for_each_entry(region, &ocelot->stats_regions, node) { 24 err = ocelot_bulk_read(ocelot, region->base, region->buf, 25 region->count); 26 if (err) 27 return err; 28 } 29 30 return 0; 31 } 32 33 /* Transfer the counters from region->buf to ocelot->stats. 34 * Caller must hold &ocelot->stat_view_lock and &ocelot->stats_lock. 35 */ 36 static void ocelot_port_transfer_stats(struct ocelot *ocelot, int port) 37 { 38 unsigned int idx = port * OCELOT_NUM_STATS; 39 struct ocelot_stats_region *region; 40 int j; 41 42 list_for_each_entry(region, &ocelot->stats_regions, node) { 43 for (j = 0; j < region->count; j++) { 44 u64 *stat = &ocelot->stats[idx + j]; 45 u64 val = region->buf[j]; 46 47 if (val < (*stat & U32_MAX)) 48 *stat += (u64)1 << 32; 49 50 *stat = (*stat & ~(u64)U32_MAX) + val; 51 } 52 53 idx += region->count; 54 } 55 } 56 57 static void ocelot_check_stats_work(struct work_struct *work) 58 { 59 struct delayed_work *del_work = to_delayed_work(work); 60 struct ocelot *ocelot = container_of(del_work, struct ocelot, 61 stats_work); 62 int port, err; 63 64 mutex_lock(&ocelot->stat_view_lock); 65 66 for (port = 0; port < ocelot->num_phys_ports; port++) { 67 err = ocelot_port_update_stats(ocelot, port); 68 if (err) 69 break; 70 71 spin_lock(&ocelot->stats_lock); 72 ocelot_port_transfer_stats(ocelot, port); 73 spin_unlock(&ocelot->stats_lock); 74 } 75 76 if (!err && ocelot->ops->update_stats) 77 ocelot->ops->update_stats(ocelot); 78 79 mutex_unlock(&ocelot->stat_view_lock); 80 81 if (err) 82 dev_err(ocelot->dev, "Error %d updating ethtool stats\n", err); 83 84 queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 85 OCELOT_STATS_CHECK_DELAY); 86 } 87 88 void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) 89 { 90 int i; 91 92 if (sset != ETH_SS_STATS) 93 return; 94 95 for (i = 0; i < OCELOT_NUM_STATS; i++) { 96 if (ocelot->stats_layout[i].name[0] == '\0') 97 continue; 98 99 memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, 100 ETH_GSTRING_LEN); 101 } 102 } 103 EXPORT_SYMBOL(ocelot_get_strings); 104 105 /* Update ocelot->stats for the given port and run the given callback */ 106 static void ocelot_port_stats_run(struct ocelot *ocelot, int port, void *priv, 107 void (*cb)(struct ocelot *ocelot, int port, 108 void *priv)) 109 { 110 int err; 111 112 mutex_lock(&ocelot->stat_view_lock); 113 114 err = ocelot_port_update_stats(ocelot, port); 115 if (err) { 116 dev_err(ocelot->dev, "Failed to update port %d stats: %pe\n", 117 port, ERR_PTR(err)); 118 goto out_unlock; 119 } 120 121 spin_lock(&ocelot->stats_lock); 122 123 ocelot_port_transfer_stats(ocelot, port); 124 cb(ocelot, port, priv); 125 126 spin_unlock(&ocelot->stats_lock); 127 128 out_unlock: 129 mutex_unlock(&ocelot->stat_view_lock); 130 } 131 132 int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) 133 { 134 int i, num_stats = 0; 135 136 if (sset != ETH_SS_STATS) 137 return -EOPNOTSUPP; 138 139 for (i = 0; i < OCELOT_NUM_STATS; i++) 140 if (ocelot->stats_layout[i].name[0] != '\0') 141 num_stats++; 142 143 return num_stats; 144 } 145 EXPORT_SYMBOL(ocelot_get_sset_count); 146 147 static void ocelot_port_ethtool_stats_cb(struct ocelot *ocelot, int port, 148 void *priv) 149 { 150 u64 *data = priv; 151 int i; 152 153 /* Copy all supported counters */ 154 for (i = 0; i < OCELOT_NUM_STATS; i++) { 155 int index = port * OCELOT_NUM_STATS + i; 156 157 if (ocelot->stats_layout[i].name[0] == '\0') 158 continue; 159 160 *data++ = ocelot->stats[index]; 161 } 162 } 163 164 void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) 165 { 166 ocelot_port_stats_run(ocelot, port, data, ocelot_port_ethtool_stats_cb); 167 } 168 EXPORT_SYMBOL(ocelot_get_ethtool_stats); 169 170 static void ocelot_port_pause_stats_cb(struct ocelot *ocelot, int port, void *priv) 171 { 172 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 173 struct ethtool_pause_stats *pause_stats = priv; 174 175 pause_stats->tx_pause_frames = s[OCELOT_STAT_TX_PAUSE]; 176 pause_stats->rx_pause_frames = s[OCELOT_STAT_RX_PAUSE]; 177 } 178 179 void ocelot_port_get_pause_stats(struct ocelot *ocelot, int port, 180 struct ethtool_pause_stats *pause_stats) 181 { 182 ocelot_port_stats_run(ocelot, port, pause_stats, 183 ocelot_port_pause_stats_cb); 184 } 185 EXPORT_SYMBOL_GPL(ocelot_port_get_pause_stats); 186 187 static const struct ethtool_rmon_hist_range ocelot_rmon_ranges[] = { 188 { 64, 64 }, 189 { 65, 127 }, 190 { 128, 255 }, 191 { 256, 511 }, 192 { 512, 1023 }, 193 { 1024, 1526 }, 194 { 1527, 65535 }, 195 {}, 196 }; 197 198 static void ocelot_port_rmon_stats_cb(struct ocelot *ocelot, int port, void *priv) 199 { 200 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 201 struct ethtool_rmon_stats *rmon_stats = priv; 202 203 rmon_stats->undersize_pkts = s[OCELOT_STAT_RX_SHORTS]; 204 rmon_stats->oversize_pkts = s[OCELOT_STAT_RX_LONGS]; 205 rmon_stats->fragments = s[OCELOT_STAT_RX_FRAGMENTS]; 206 rmon_stats->jabbers = s[OCELOT_STAT_RX_JABBERS]; 207 208 rmon_stats->hist[0] = s[OCELOT_STAT_RX_64]; 209 rmon_stats->hist[1] = s[OCELOT_STAT_RX_65_127]; 210 rmon_stats->hist[2] = s[OCELOT_STAT_RX_128_255]; 211 rmon_stats->hist[3] = s[OCELOT_STAT_RX_256_511]; 212 rmon_stats->hist[4] = s[OCELOT_STAT_RX_512_1023]; 213 rmon_stats->hist[5] = s[OCELOT_STAT_RX_1024_1526]; 214 rmon_stats->hist[6] = s[OCELOT_STAT_RX_1527_MAX]; 215 216 rmon_stats->hist_tx[0] = s[OCELOT_STAT_TX_64]; 217 rmon_stats->hist_tx[1] = s[OCELOT_STAT_TX_65_127]; 218 rmon_stats->hist_tx[2] = s[OCELOT_STAT_TX_128_255]; 219 rmon_stats->hist_tx[3] = s[OCELOT_STAT_TX_128_255]; 220 rmon_stats->hist_tx[4] = s[OCELOT_STAT_TX_256_511]; 221 rmon_stats->hist_tx[5] = s[OCELOT_STAT_TX_512_1023]; 222 rmon_stats->hist_tx[6] = s[OCELOT_STAT_TX_1024_1526]; 223 } 224 225 void ocelot_port_get_rmon_stats(struct ocelot *ocelot, int port, 226 struct ethtool_rmon_stats *rmon_stats, 227 const struct ethtool_rmon_hist_range **ranges) 228 { 229 *ranges = ocelot_rmon_ranges; 230 231 ocelot_port_stats_run(ocelot, port, rmon_stats, 232 ocelot_port_rmon_stats_cb); 233 } 234 EXPORT_SYMBOL_GPL(ocelot_port_get_rmon_stats); 235 236 static void ocelot_port_ctrl_stats_cb(struct ocelot *ocelot, int port, void *priv) 237 { 238 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 239 struct ethtool_eth_ctrl_stats *ctrl_stats = priv; 240 241 ctrl_stats->MACControlFramesReceived = s[OCELOT_STAT_RX_CONTROL]; 242 } 243 244 void ocelot_port_get_eth_ctrl_stats(struct ocelot *ocelot, int port, 245 struct ethtool_eth_ctrl_stats *ctrl_stats) 246 { 247 ocelot_port_stats_run(ocelot, port, ctrl_stats, 248 ocelot_port_ctrl_stats_cb); 249 } 250 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_ctrl_stats); 251 252 static void ocelot_port_mac_stats_cb(struct ocelot *ocelot, int port, void *priv) 253 { 254 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 255 struct ethtool_eth_mac_stats *mac_stats = priv; 256 257 mac_stats->OctetsTransmittedOK = s[OCELOT_STAT_TX_OCTETS]; 258 mac_stats->FramesTransmittedOK = s[OCELOT_STAT_TX_64] + 259 s[OCELOT_STAT_TX_65_127] + 260 s[OCELOT_STAT_TX_128_255] + 261 s[OCELOT_STAT_TX_256_511] + 262 s[OCELOT_STAT_TX_512_1023] + 263 s[OCELOT_STAT_TX_1024_1526] + 264 s[OCELOT_STAT_TX_1527_MAX]; 265 mac_stats->OctetsReceivedOK = s[OCELOT_STAT_RX_OCTETS]; 266 mac_stats->FramesReceivedOK = s[OCELOT_STAT_RX_GREEN_PRIO_0] + 267 s[OCELOT_STAT_RX_GREEN_PRIO_1] + 268 s[OCELOT_STAT_RX_GREEN_PRIO_2] + 269 s[OCELOT_STAT_RX_GREEN_PRIO_3] + 270 s[OCELOT_STAT_RX_GREEN_PRIO_4] + 271 s[OCELOT_STAT_RX_GREEN_PRIO_5] + 272 s[OCELOT_STAT_RX_GREEN_PRIO_6] + 273 s[OCELOT_STAT_RX_GREEN_PRIO_7] + 274 s[OCELOT_STAT_RX_YELLOW_PRIO_0] + 275 s[OCELOT_STAT_RX_YELLOW_PRIO_1] + 276 s[OCELOT_STAT_RX_YELLOW_PRIO_2] + 277 s[OCELOT_STAT_RX_YELLOW_PRIO_3] + 278 s[OCELOT_STAT_RX_YELLOW_PRIO_4] + 279 s[OCELOT_STAT_RX_YELLOW_PRIO_5] + 280 s[OCELOT_STAT_RX_YELLOW_PRIO_6] + 281 s[OCELOT_STAT_RX_YELLOW_PRIO_7]; 282 mac_stats->MulticastFramesXmittedOK = s[OCELOT_STAT_TX_MULTICAST]; 283 mac_stats->BroadcastFramesXmittedOK = s[OCELOT_STAT_TX_BROADCAST]; 284 mac_stats->MulticastFramesReceivedOK = s[OCELOT_STAT_RX_MULTICAST]; 285 mac_stats->BroadcastFramesReceivedOK = s[OCELOT_STAT_RX_BROADCAST]; 286 mac_stats->FrameTooLongErrors = s[OCELOT_STAT_RX_LONGS]; 287 /* Sadly, C_RX_CRC is the sum of FCS and alignment errors, they are not 288 * counted individually. 289 */ 290 mac_stats->FrameCheckSequenceErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; 291 mac_stats->AlignmentErrors = s[OCELOT_STAT_RX_CRC_ALIGN_ERRS]; 292 } 293 294 void ocelot_port_get_eth_mac_stats(struct ocelot *ocelot, int port, 295 struct ethtool_eth_mac_stats *mac_stats) 296 { 297 ocelot_port_stats_run(ocelot, port, mac_stats, 298 ocelot_port_mac_stats_cb); 299 } 300 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_mac_stats); 301 302 static void ocelot_port_phy_stats_cb(struct ocelot *ocelot, int port, void *priv) 303 { 304 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 305 struct ethtool_eth_phy_stats *phy_stats = priv; 306 307 phy_stats->SymbolErrorDuringCarrier = s[OCELOT_STAT_RX_SYM_ERRS]; 308 } 309 310 void ocelot_port_get_eth_phy_stats(struct ocelot *ocelot, int port, 311 struct ethtool_eth_phy_stats *phy_stats) 312 { 313 ocelot_port_stats_run(ocelot, port, phy_stats, 314 ocelot_port_phy_stats_cb); 315 } 316 EXPORT_SYMBOL_GPL(ocelot_port_get_eth_phy_stats); 317 318 void ocelot_port_get_stats64(struct ocelot *ocelot, int port, 319 struct rtnl_link_stats64 *stats) 320 { 321 u64 *s = &ocelot->stats[port * OCELOT_NUM_STATS]; 322 323 spin_lock(&ocelot->stats_lock); 324 325 /* Get Rx stats */ 326 stats->rx_bytes = s[OCELOT_STAT_RX_OCTETS]; 327 stats->rx_packets = s[OCELOT_STAT_RX_SHORTS] + 328 s[OCELOT_STAT_RX_FRAGMENTS] + 329 s[OCELOT_STAT_RX_JABBERS] + 330 s[OCELOT_STAT_RX_LONGS] + 331 s[OCELOT_STAT_RX_64] + 332 s[OCELOT_STAT_RX_65_127] + 333 s[OCELOT_STAT_RX_128_255] + 334 s[OCELOT_STAT_RX_256_511] + 335 s[OCELOT_STAT_RX_512_1023] + 336 s[OCELOT_STAT_RX_1024_1526] + 337 s[OCELOT_STAT_RX_1527_MAX]; 338 stats->multicast = s[OCELOT_STAT_RX_MULTICAST]; 339 stats->rx_missed_errors = s[OCELOT_STAT_DROP_TAIL]; 340 stats->rx_dropped = s[OCELOT_STAT_RX_RED_PRIO_0] + 341 s[OCELOT_STAT_RX_RED_PRIO_1] + 342 s[OCELOT_STAT_RX_RED_PRIO_2] + 343 s[OCELOT_STAT_RX_RED_PRIO_3] + 344 s[OCELOT_STAT_RX_RED_PRIO_4] + 345 s[OCELOT_STAT_RX_RED_PRIO_5] + 346 s[OCELOT_STAT_RX_RED_PRIO_6] + 347 s[OCELOT_STAT_RX_RED_PRIO_7] + 348 s[OCELOT_STAT_DROP_LOCAL] + 349 s[OCELOT_STAT_DROP_YELLOW_PRIO_0] + 350 s[OCELOT_STAT_DROP_YELLOW_PRIO_1] + 351 s[OCELOT_STAT_DROP_YELLOW_PRIO_2] + 352 s[OCELOT_STAT_DROP_YELLOW_PRIO_3] + 353 s[OCELOT_STAT_DROP_YELLOW_PRIO_4] + 354 s[OCELOT_STAT_DROP_YELLOW_PRIO_5] + 355 s[OCELOT_STAT_DROP_YELLOW_PRIO_6] + 356 s[OCELOT_STAT_DROP_YELLOW_PRIO_7] + 357 s[OCELOT_STAT_DROP_GREEN_PRIO_0] + 358 s[OCELOT_STAT_DROP_GREEN_PRIO_1] + 359 s[OCELOT_STAT_DROP_GREEN_PRIO_2] + 360 s[OCELOT_STAT_DROP_GREEN_PRIO_3] + 361 s[OCELOT_STAT_DROP_GREEN_PRIO_4] + 362 s[OCELOT_STAT_DROP_GREEN_PRIO_5] + 363 s[OCELOT_STAT_DROP_GREEN_PRIO_6] + 364 s[OCELOT_STAT_DROP_GREEN_PRIO_7]; 365 366 /* Get Tx stats */ 367 stats->tx_bytes = s[OCELOT_STAT_TX_OCTETS]; 368 stats->tx_packets = s[OCELOT_STAT_TX_64] + 369 s[OCELOT_STAT_TX_65_127] + 370 s[OCELOT_STAT_TX_128_255] + 371 s[OCELOT_STAT_TX_256_511] + 372 s[OCELOT_STAT_TX_512_1023] + 373 s[OCELOT_STAT_TX_1024_1526] + 374 s[OCELOT_STAT_TX_1527_MAX]; 375 stats->tx_dropped = s[OCELOT_STAT_TX_DROPS] + 376 s[OCELOT_STAT_TX_AGED]; 377 stats->collisions = s[OCELOT_STAT_TX_COLLISION]; 378 379 spin_unlock(&ocelot->stats_lock); 380 } 381 EXPORT_SYMBOL(ocelot_port_get_stats64); 382 383 static int ocelot_prepare_stats_regions(struct ocelot *ocelot) 384 { 385 struct ocelot_stats_region *region = NULL; 386 unsigned int last; 387 int i; 388 389 INIT_LIST_HEAD(&ocelot->stats_regions); 390 391 for (i = 0; i < OCELOT_NUM_STATS; i++) { 392 if (!ocelot->stats_layout[i].reg) 393 continue; 394 395 if (region && ocelot->stats_layout[i].reg == last + 4) { 396 region->count++; 397 } else { 398 region = devm_kzalloc(ocelot->dev, sizeof(*region), 399 GFP_KERNEL); 400 if (!region) 401 return -ENOMEM; 402 403 region->base = ocelot->stats_layout[i].reg; 404 region->count = 1; 405 list_add_tail(®ion->node, &ocelot->stats_regions); 406 } 407 408 last = ocelot->stats_layout[i].reg; 409 } 410 411 list_for_each_entry(region, &ocelot->stats_regions, node) { 412 region->buf = devm_kcalloc(ocelot->dev, region->count, 413 sizeof(*region->buf), GFP_KERNEL); 414 if (!region->buf) 415 return -ENOMEM; 416 } 417 418 return 0; 419 } 420 421 int ocelot_stats_init(struct ocelot *ocelot) 422 { 423 char queue_name[32]; 424 int ret; 425 426 ocelot->stats = devm_kcalloc(ocelot->dev, 427 ocelot->num_phys_ports * OCELOT_NUM_STATS, 428 sizeof(u64), GFP_KERNEL); 429 if (!ocelot->stats) 430 return -ENOMEM; 431 432 snprintf(queue_name, sizeof(queue_name), "%s-stats", 433 dev_name(ocelot->dev)); 434 ocelot->stats_queue = create_singlethread_workqueue(queue_name); 435 if (!ocelot->stats_queue) 436 return -ENOMEM; 437 438 spin_lock_init(&ocelot->stats_lock); 439 mutex_init(&ocelot->stat_view_lock); 440 441 ret = ocelot_prepare_stats_regions(ocelot); 442 if (ret) { 443 destroy_workqueue(ocelot->stats_queue); 444 return ret; 445 } 446 447 INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work); 448 queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work, 449 OCELOT_STATS_CHECK_DELAY); 450 451 return 0; 452 } 453 454 void ocelot_stats_deinit(struct ocelot *ocelot) 455 { 456 cancel_delayed_work(&ocelot->stats_work); 457 destroy_workqueue(ocelot->stats_queue); 458 } 459