1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */ 3 4 #include "tsnep.h" 5 6 static const char tsnep_stats_strings[][ETH_GSTRING_LEN] = { 7 "rx_packets", 8 "rx_bytes", 9 "rx_dropped", 10 "rx_multicast", 11 "rx_alloc_failed", 12 "rx_phy_errors", 13 "rx_forwarded_phy_errors", 14 "rx_invalid_frame_errors", 15 "tx_packets", 16 "tx_bytes", 17 "tx_dropped", 18 }; 19 20 struct tsnep_stats { 21 u64 rx_packets; 22 u64 rx_bytes; 23 u64 rx_dropped; 24 u64 rx_multicast; 25 u64 rx_alloc_failed; 26 u64 rx_phy_errors; 27 u64 rx_forwarded_phy_errors; 28 u64 rx_invalid_frame_errors; 29 u64 tx_packets; 30 u64 tx_bytes; 31 u64 tx_dropped; 32 }; 33 34 #define TSNEP_STATS_COUNT (sizeof(struct tsnep_stats) / sizeof(u64)) 35 36 static const char tsnep_rx_queue_stats_strings[][ETH_GSTRING_LEN] = { 37 "rx_%d_packets", 38 "rx_%d_bytes", 39 "rx_%d_dropped", 40 "rx_%d_multicast", 41 "rx_%d_alloc_failed", 42 "rx_%d_no_descriptor_errors", 43 "rx_%d_buffer_too_small_errors", 44 "rx_%d_fifo_overflow_errors", 45 "rx_%d_invalid_frame_errors", 46 }; 47 48 struct tsnep_rx_queue_stats { 49 u64 rx_packets; 50 u64 rx_bytes; 51 u64 rx_dropped; 52 u64 rx_multicast; 53 u64 rx_alloc_failed; 54 u64 rx_no_descriptor_errors; 55 u64 rx_buffer_too_small_errors; 56 u64 rx_fifo_overflow_errors; 57 u64 rx_invalid_frame_errors; 58 }; 59 60 #define TSNEP_RX_QUEUE_STATS_COUNT (sizeof(struct tsnep_rx_queue_stats) / \ 61 sizeof(u64)) 62 63 static const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = { 64 "tx_%d_packets", 65 "tx_%d_bytes", 66 "tx_%d_dropped", 67 }; 68 69 struct tsnep_tx_queue_stats { 70 u64 tx_packets; 71 u64 tx_bytes; 72 u64 tx_dropped; 73 }; 74 75 #define TSNEP_TX_QUEUE_STATS_COUNT (sizeof(struct tsnep_tx_queue_stats) / \ 76 sizeof(u64)) 77 78 static void tsnep_ethtool_get_drvinfo(struct net_device *netdev, 79 struct ethtool_drvinfo *drvinfo) 80 { 81 struct tsnep_adapter *adapter = netdev_priv(netdev); 82 83 strscpy(drvinfo->driver, TSNEP, sizeof(drvinfo->driver)); 84 strscpy(drvinfo->bus_info, dev_name(&adapter->pdev->dev), 85 sizeof(drvinfo->bus_info)); 86 } 87 88 static int tsnep_ethtool_get_regs_len(struct net_device *netdev) 89 { 90 struct tsnep_adapter *adapter = netdev_priv(netdev); 91 int len; 92 int num_additional_queues; 93 94 len = TSNEP_MAC_SIZE; 95 96 /* first queue pair is within TSNEP_MAC_SIZE, only queues additional to 97 * the first queue pair extend the register length by TSNEP_QUEUE_SIZE 98 */ 99 num_additional_queues = 100 max(adapter->num_tx_queues, adapter->num_rx_queues) - 1; 101 len += TSNEP_QUEUE_SIZE * num_additional_queues; 102 103 return len; 104 } 105 106 static void tsnep_ethtool_get_regs(struct net_device *netdev, 107 struct ethtool_regs *regs, 108 void *p) 109 { 110 struct tsnep_adapter *adapter = netdev_priv(netdev); 111 112 regs->version = 1; 113 114 memcpy_fromio(p, adapter->addr, regs->len); 115 } 116 117 static u32 tsnep_ethtool_get_msglevel(struct net_device *netdev) 118 { 119 struct tsnep_adapter *adapter = netdev_priv(netdev); 120 121 return adapter->msg_enable; 122 } 123 124 static void tsnep_ethtool_set_msglevel(struct net_device *netdev, u32 data) 125 { 126 struct tsnep_adapter *adapter = netdev_priv(netdev); 127 128 adapter->msg_enable = data; 129 } 130 131 static void tsnep_ethtool_get_strings(struct net_device *netdev, u32 stringset, 132 u8 *data) 133 { 134 struct tsnep_adapter *adapter = netdev_priv(netdev); 135 int rx_count = adapter->num_rx_queues; 136 int tx_count = adapter->num_tx_queues; 137 int i, j; 138 139 switch (stringset) { 140 case ETH_SS_STATS: 141 memcpy(data, tsnep_stats_strings, sizeof(tsnep_stats_strings)); 142 data += sizeof(tsnep_stats_strings); 143 144 for (i = 0; i < rx_count; i++) { 145 for (j = 0; j < TSNEP_RX_QUEUE_STATS_COUNT; j++) { 146 snprintf(data, ETH_GSTRING_LEN, 147 tsnep_rx_queue_stats_strings[j], i); 148 data += ETH_GSTRING_LEN; 149 } 150 } 151 152 for (i = 0; i < tx_count; i++) { 153 for (j = 0; j < TSNEP_TX_QUEUE_STATS_COUNT; j++) { 154 snprintf(data, ETH_GSTRING_LEN, 155 tsnep_tx_queue_stats_strings[j], i); 156 data += ETH_GSTRING_LEN; 157 } 158 } 159 break; 160 case ETH_SS_TEST: 161 tsnep_ethtool_get_test_strings(data); 162 break; 163 } 164 } 165 166 static void tsnep_ethtool_get_ethtool_stats(struct net_device *netdev, 167 struct ethtool_stats *stats, 168 u64 *data) 169 { 170 struct tsnep_adapter *adapter = netdev_priv(netdev); 171 int rx_count = adapter->num_rx_queues; 172 int tx_count = adapter->num_tx_queues; 173 struct tsnep_stats tsnep_stats; 174 struct tsnep_rx_queue_stats tsnep_rx_queue_stats; 175 struct tsnep_tx_queue_stats tsnep_tx_queue_stats; 176 u32 reg; 177 int i; 178 179 memset(&tsnep_stats, 0, sizeof(tsnep_stats)); 180 for (i = 0; i < adapter->num_rx_queues; i++) { 181 tsnep_stats.rx_packets += adapter->rx[i].packets; 182 tsnep_stats.rx_bytes += adapter->rx[i].bytes; 183 tsnep_stats.rx_dropped += adapter->rx[i].dropped; 184 tsnep_stats.rx_multicast += adapter->rx[i].multicast; 185 tsnep_stats.rx_alloc_failed += adapter->rx[i].alloc_failed; 186 } 187 reg = ioread32(adapter->addr + ECM_STAT); 188 tsnep_stats.rx_phy_errors = 189 (reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT; 190 tsnep_stats.rx_forwarded_phy_errors = 191 (reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT; 192 tsnep_stats.rx_invalid_frame_errors = 193 (reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT; 194 for (i = 0; i < adapter->num_tx_queues; i++) { 195 tsnep_stats.tx_packets += adapter->tx[i].packets; 196 tsnep_stats.tx_bytes += adapter->tx[i].bytes; 197 tsnep_stats.tx_dropped += adapter->tx[i].dropped; 198 } 199 memcpy(data, &tsnep_stats, sizeof(tsnep_stats)); 200 data += TSNEP_STATS_COUNT; 201 202 for (i = 0; i < rx_count; i++) { 203 memset(&tsnep_rx_queue_stats, 0, sizeof(tsnep_rx_queue_stats)); 204 tsnep_rx_queue_stats.rx_packets = adapter->rx[i].packets; 205 tsnep_rx_queue_stats.rx_bytes = adapter->rx[i].bytes; 206 tsnep_rx_queue_stats.rx_dropped = adapter->rx[i].dropped; 207 tsnep_rx_queue_stats.rx_multicast = adapter->rx[i].multicast; 208 tsnep_rx_queue_stats.rx_alloc_failed = 209 adapter->rx[i].alloc_failed; 210 reg = ioread32(adapter->addr + TSNEP_QUEUE(i) + 211 TSNEP_RX_STATISTIC); 212 tsnep_rx_queue_stats.rx_no_descriptor_errors = 213 (reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >> 214 TSNEP_RX_STATISTIC_NO_DESC_SHIFT; 215 tsnep_rx_queue_stats.rx_buffer_too_small_errors = 216 (reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >> 217 TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT; 218 tsnep_rx_queue_stats.rx_fifo_overflow_errors = 219 (reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >> 220 TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT; 221 tsnep_rx_queue_stats.rx_invalid_frame_errors = 222 (reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >> 223 TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT; 224 memcpy(data, &tsnep_rx_queue_stats, 225 sizeof(tsnep_rx_queue_stats)); 226 data += TSNEP_RX_QUEUE_STATS_COUNT; 227 } 228 229 for (i = 0; i < tx_count; i++) { 230 memset(&tsnep_tx_queue_stats, 0, sizeof(tsnep_tx_queue_stats)); 231 tsnep_tx_queue_stats.tx_packets += adapter->tx[i].packets; 232 tsnep_tx_queue_stats.tx_bytes += adapter->tx[i].bytes; 233 tsnep_tx_queue_stats.tx_dropped += adapter->tx[i].dropped; 234 memcpy(data, &tsnep_tx_queue_stats, 235 sizeof(tsnep_tx_queue_stats)); 236 data += TSNEP_TX_QUEUE_STATS_COUNT; 237 } 238 } 239 240 static int tsnep_ethtool_get_sset_count(struct net_device *netdev, int sset) 241 { 242 struct tsnep_adapter *adapter = netdev_priv(netdev); 243 int rx_count; 244 int tx_count; 245 246 switch (sset) { 247 case ETH_SS_STATS: 248 rx_count = adapter->num_rx_queues; 249 tx_count = adapter->num_tx_queues; 250 return TSNEP_STATS_COUNT + 251 TSNEP_RX_QUEUE_STATS_COUNT * rx_count + 252 TSNEP_TX_QUEUE_STATS_COUNT * tx_count; 253 case ETH_SS_TEST: 254 return tsnep_ethtool_get_test_count(); 255 default: 256 return -EOPNOTSUPP; 257 } 258 } 259 260 static int tsnep_ethtool_get_rxnfc(struct net_device *netdev, 261 struct ethtool_rxnfc *cmd, u32 *rule_locs) 262 { 263 struct tsnep_adapter *adapter = netdev_priv(netdev); 264 265 switch (cmd->cmd) { 266 case ETHTOOL_GRXRINGS: 267 cmd->data = adapter->num_rx_queues; 268 return 0; 269 case ETHTOOL_GRXCLSRLCNT: 270 cmd->rule_cnt = adapter->rxnfc_count; 271 cmd->data = adapter->rxnfc_max; 272 cmd->data |= RX_CLS_LOC_SPECIAL; 273 return 0; 274 case ETHTOOL_GRXCLSRULE: 275 return tsnep_rxnfc_get_rule(adapter, cmd); 276 case ETHTOOL_GRXCLSRLALL: 277 return tsnep_rxnfc_get_all(adapter, cmd, rule_locs); 278 default: 279 return -EOPNOTSUPP; 280 } 281 } 282 283 static int tsnep_ethtool_set_rxnfc(struct net_device *netdev, 284 struct ethtool_rxnfc *cmd) 285 { 286 struct tsnep_adapter *adapter = netdev_priv(netdev); 287 288 switch (cmd->cmd) { 289 case ETHTOOL_SRXCLSRLINS: 290 return tsnep_rxnfc_add_rule(adapter, cmd); 291 case ETHTOOL_SRXCLSRLDEL: 292 return tsnep_rxnfc_del_rule(adapter, cmd); 293 default: 294 return -EOPNOTSUPP; 295 } 296 } 297 298 static void tsnep_ethtool_get_channels(struct net_device *netdev, 299 struct ethtool_channels *ch) 300 { 301 struct tsnep_adapter *adapter = netdev_priv(netdev); 302 303 ch->max_rx = adapter->num_rx_queues; 304 ch->max_tx = adapter->num_tx_queues; 305 ch->rx_count = adapter->num_rx_queues; 306 ch->tx_count = adapter->num_tx_queues; 307 } 308 309 static int tsnep_ethtool_get_ts_info(struct net_device *netdev, 310 struct ethtool_ts_info *info) 311 { 312 struct tsnep_adapter *adapter = netdev_priv(netdev); 313 314 info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 315 SOF_TIMESTAMPING_RX_SOFTWARE | 316 SOF_TIMESTAMPING_SOFTWARE | 317 SOF_TIMESTAMPING_TX_HARDWARE | 318 SOF_TIMESTAMPING_RX_HARDWARE | 319 SOF_TIMESTAMPING_RAW_HARDWARE; 320 321 if (adapter->ptp_clock) 322 info->phc_index = ptp_clock_index(adapter->ptp_clock); 323 else 324 info->phc_index = -1; 325 326 info->tx_types = BIT(HWTSTAMP_TX_OFF) | 327 BIT(HWTSTAMP_TX_ON); 328 info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 329 BIT(HWTSTAMP_FILTER_ALL); 330 331 return 0; 332 } 333 334 static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter, 335 int index) 336 { 337 int i; 338 339 for (i = 0; i < adapter->num_queues; i++) { 340 if (adapter->queue[i].tx) { 341 if (index == 0) 342 return &adapter->queue[i]; 343 344 index--; 345 } 346 } 347 348 return NULL; 349 } 350 351 static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter, 352 int index) 353 { 354 int i; 355 356 for (i = 0; i < adapter->num_queues; i++) { 357 if (adapter->queue[i].rx) { 358 if (index == 0) 359 return &adapter->queue[i]; 360 361 index--; 362 } 363 } 364 365 return NULL; 366 } 367 368 static int tsnep_ethtool_get_coalesce(struct net_device *netdev, 369 struct ethtool_coalesce *ec, 370 struct kernel_ethtool_coalesce *kernel_coal, 371 struct netlink_ext_ack *extack) 372 { 373 struct tsnep_adapter *adapter = netdev_priv(netdev); 374 struct tsnep_queue *queue; 375 376 queue = tsnep_get_queue_with_rx(adapter, 0); 377 if (queue) 378 ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue); 379 380 queue = tsnep_get_queue_with_tx(adapter, 0); 381 if (queue) 382 ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue); 383 384 return 0; 385 } 386 387 static int tsnep_ethtool_set_coalesce(struct net_device *netdev, 388 struct ethtool_coalesce *ec, 389 struct kernel_ethtool_coalesce *kernel_coal, 390 struct netlink_ext_ack *extack) 391 { 392 struct tsnep_adapter *adapter = netdev_priv(netdev); 393 int i; 394 int retval; 395 396 for (i = 0; i < adapter->num_queues; i++) { 397 /* RX coalesce has priority for queues with TX and RX */ 398 if (adapter->queue[i].rx) 399 retval = tsnep_set_irq_coalesce(&adapter->queue[i], 400 ec->rx_coalesce_usecs); 401 else 402 retval = tsnep_set_irq_coalesce(&adapter->queue[i], 403 ec->tx_coalesce_usecs); 404 if (retval != 0) 405 return retval; 406 } 407 408 return 0; 409 } 410 411 static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev, 412 u32 queue, 413 struct ethtool_coalesce *ec) 414 { 415 struct tsnep_adapter *adapter = netdev_priv(netdev); 416 struct tsnep_queue *queue_with_rx; 417 struct tsnep_queue *queue_with_tx; 418 419 if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues)) 420 return -EINVAL; 421 422 queue_with_rx = tsnep_get_queue_with_rx(adapter, queue); 423 if (queue_with_rx) 424 ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx); 425 426 queue_with_tx = tsnep_get_queue_with_tx(adapter, queue); 427 if (queue_with_tx) 428 ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx); 429 430 return 0; 431 } 432 433 static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev, 434 u32 queue, 435 struct ethtool_coalesce *ec) 436 { 437 struct tsnep_adapter *adapter = netdev_priv(netdev); 438 struct tsnep_queue *queue_with_rx; 439 struct tsnep_queue *queue_with_tx; 440 int retval; 441 442 if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues)) 443 return -EINVAL; 444 445 queue_with_rx = tsnep_get_queue_with_rx(adapter, queue); 446 if (queue_with_rx) { 447 retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs); 448 if (retval != 0) 449 return retval; 450 } 451 452 /* RX coalesce has priority for queues with TX and RX */ 453 queue_with_tx = tsnep_get_queue_with_tx(adapter, queue); 454 if (queue_with_tx && !queue_with_tx->rx) { 455 retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs); 456 if (retval != 0) 457 return retval; 458 } 459 460 return 0; 461 } 462 463 const struct ethtool_ops tsnep_ethtool_ops = { 464 .supported_coalesce_params = ETHTOOL_COALESCE_USECS, 465 .get_drvinfo = tsnep_ethtool_get_drvinfo, 466 .get_regs_len = tsnep_ethtool_get_regs_len, 467 .get_regs = tsnep_ethtool_get_regs, 468 .get_msglevel = tsnep_ethtool_get_msglevel, 469 .set_msglevel = tsnep_ethtool_set_msglevel, 470 .nway_reset = phy_ethtool_nway_reset, 471 .get_link = ethtool_op_get_link, 472 .self_test = tsnep_ethtool_self_test, 473 .get_strings = tsnep_ethtool_get_strings, 474 .get_ethtool_stats = tsnep_ethtool_get_ethtool_stats, 475 .get_sset_count = tsnep_ethtool_get_sset_count, 476 .get_rxnfc = tsnep_ethtool_get_rxnfc, 477 .set_rxnfc = tsnep_ethtool_set_rxnfc, 478 .get_channels = tsnep_ethtool_get_channels, 479 .get_ts_info = tsnep_ethtool_get_ts_info, 480 .get_coalesce = tsnep_ethtool_get_coalesce, 481 .set_coalesce = tsnep_ethtool_set_coalesce, 482 .get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce, 483 .set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce, 484 .get_link_ksettings = phy_ethtool_get_link_ksettings, 485 .set_link_ksettings = phy_ethtool_set_link_ksettings, 486 }; 487