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_combined = adapter->num_queues; 304 ch->combined_count = adapter->num_queues; 305 } 306 307 static int tsnep_ethtool_get_ts_info(struct net_device *netdev, 308 struct kernel_ethtool_ts_info *info) 309 { 310 struct tsnep_adapter *adapter = netdev_priv(netdev); 311 312 info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | 313 SOF_TIMESTAMPING_RX_SOFTWARE | 314 SOF_TIMESTAMPING_SOFTWARE | 315 SOF_TIMESTAMPING_TX_HARDWARE | 316 SOF_TIMESTAMPING_RX_HARDWARE | 317 SOF_TIMESTAMPING_RAW_HARDWARE; 318 319 if (adapter->ptp_clock) 320 info->phc_index = ptp_clock_index(adapter->ptp_clock); 321 else 322 info->phc_index = -1; 323 324 info->tx_types = BIT(HWTSTAMP_TX_OFF) | 325 BIT(HWTSTAMP_TX_ON); 326 info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | 327 BIT(HWTSTAMP_FILTER_ALL); 328 329 return 0; 330 } 331 332 static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter, 333 int index) 334 { 335 int i; 336 337 for (i = 0; i < adapter->num_queues; i++) { 338 if (adapter->queue[i].tx) { 339 if (index == 0) 340 return &adapter->queue[i]; 341 342 index--; 343 } 344 } 345 346 return NULL; 347 } 348 349 static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter, 350 int index) 351 { 352 int i; 353 354 for (i = 0; i < adapter->num_queues; i++) { 355 if (adapter->queue[i].rx) { 356 if (index == 0) 357 return &adapter->queue[i]; 358 359 index--; 360 } 361 } 362 363 return NULL; 364 } 365 366 static int tsnep_ethtool_get_coalesce(struct net_device *netdev, 367 struct ethtool_coalesce *ec, 368 struct kernel_ethtool_coalesce *kernel_coal, 369 struct netlink_ext_ack *extack) 370 { 371 struct tsnep_adapter *adapter = netdev_priv(netdev); 372 struct tsnep_queue *queue; 373 374 queue = tsnep_get_queue_with_rx(adapter, 0); 375 if (queue) 376 ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue); 377 378 queue = tsnep_get_queue_with_tx(adapter, 0); 379 if (queue) 380 ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue); 381 382 return 0; 383 } 384 385 static int tsnep_ethtool_set_coalesce(struct net_device *netdev, 386 struct ethtool_coalesce *ec, 387 struct kernel_ethtool_coalesce *kernel_coal, 388 struct netlink_ext_ack *extack) 389 { 390 struct tsnep_adapter *adapter = netdev_priv(netdev); 391 int i; 392 int retval; 393 394 for (i = 0; i < adapter->num_queues; i++) { 395 /* RX coalesce has priority for queues with TX and RX */ 396 if (adapter->queue[i].rx) 397 retval = tsnep_set_irq_coalesce(&adapter->queue[i], 398 ec->rx_coalesce_usecs); 399 else 400 retval = tsnep_set_irq_coalesce(&adapter->queue[i], 401 ec->tx_coalesce_usecs); 402 if (retval != 0) 403 return retval; 404 } 405 406 return 0; 407 } 408 409 static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev, 410 u32 queue, 411 struct ethtool_coalesce *ec) 412 { 413 struct tsnep_adapter *adapter = netdev_priv(netdev); 414 struct tsnep_queue *queue_with_rx; 415 struct tsnep_queue *queue_with_tx; 416 417 if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues)) 418 return -EINVAL; 419 420 queue_with_rx = tsnep_get_queue_with_rx(adapter, queue); 421 if (queue_with_rx) 422 ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx); 423 424 queue_with_tx = tsnep_get_queue_with_tx(adapter, queue); 425 if (queue_with_tx) 426 ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx); 427 428 return 0; 429 } 430 431 static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev, 432 u32 queue, 433 struct ethtool_coalesce *ec) 434 { 435 struct tsnep_adapter *adapter = netdev_priv(netdev); 436 struct tsnep_queue *queue_with_rx; 437 struct tsnep_queue *queue_with_tx; 438 int retval; 439 440 if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues)) 441 return -EINVAL; 442 443 queue_with_rx = tsnep_get_queue_with_rx(adapter, queue); 444 if (queue_with_rx) { 445 retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs); 446 if (retval != 0) 447 return retval; 448 } 449 450 /* RX coalesce has priority for queues with TX and RX */ 451 queue_with_tx = tsnep_get_queue_with_tx(adapter, queue); 452 if (queue_with_tx && !queue_with_tx->rx) { 453 retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs); 454 if (retval != 0) 455 return retval; 456 } 457 458 return 0; 459 } 460 461 const struct ethtool_ops tsnep_ethtool_ops = { 462 .supported_coalesce_params = ETHTOOL_COALESCE_USECS, 463 .get_drvinfo = tsnep_ethtool_get_drvinfo, 464 .get_regs_len = tsnep_ethtool_get_regs_len, 465 .get_regs = tsnep_ethtool_get_regs, 466 .get_msglevel = tsnep_ethtool_get_msglevel, 467 .set_msglevel = tsnep_ethtool_set_msglevel, 468 .nway_reset = phy_ethtool_nway_reset, 469 .get_link = ethtool_op_get_link, 470 .self_test = tsnep_ethtool_self_test, 471 .get_strings = tsnep_ethtool_get_strings, 472 .get_ethtool_stats = tsnep_ethtool_get_ethtool_stats, 473 .get_sset_count = tsnep_ethtool_get_sset_count, 474 .get_rxnfc = tsnep_ethtool_get_rxnfc, 475 .set_rxnfc = tsnep_ethtool_set_rxnfc, 476 .get_channels = tsnep_ethtool_get_channels, 477 .get_ts_info = tsnep_ethtool_get_ts_info, 478 .get_coalesce = tsnep_ethtool_get_coalesce, 479 .set_coalesce = tsnep_ethtool_set_coalesce, 480 .get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce, 481 .set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce, 482 .get_link_ksettings = phy_ethtool_get_link_ksettings, 483 .set_link_ksettings = phy_ethtool_set_link_ksettings, 484 }; 485