1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */ 3 4#include "tsnep.h" 5 6static 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 20struct 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 36static 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 48struct 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 63static const char tsnep_tx_queue_stats_strings[][ETH_GSTRING_LEN] = { 64 "tx_%d_packets", 65 "tx_%d_bytes", 66 "tx_%d_dropped", 67}; 68 69struct 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 78static 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 88static 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 106static 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 117static 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 124static 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 131static 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 166static 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 240static 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 260static 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 283static 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 298static 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 307static int tsnep_ethtool_get_ts_info(struct net_device *netdev, 308 struct 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 332static 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 349static 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 366static 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 385static 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 409static 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 431static 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 461const 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