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