| // SPDX-License-Identifier: GPL-2.0 | |
| // | |
| // Copyright (c) 2023, 2024 Pengutronix, | |
| // Marc Kleine-Budde <kernel@pengutronix.de> | |
| // | |
| #include <linux/ethtool.h> | |
| #include "rockchip_canfd.h" | |
| enum rkcanfd_stats_type { | |
| RKCANFD_STATS_TYPE_RX_FIFO_EMPTY_ERRORS, | |
| RKCANFD_STATS_TYPE_TX_EXTENDED_AS_STANDARD_ERRORS, | |
| }; | |
| static const char rkcanfd_stats_strings[][ETH_GSTRING_LEN] = { | |
| [RKCANFD_STATS_TYPE_RX_FIFO_EMPTY_ERRORS] = "rx_fifo_empty_errors", | |
| [RKCANFD_STATS_TYPE_TX_EXTENDED_AS_STANDARD_ERRORS] = "tx_extended_as_standard_errors", | |
| }; | |
| static void | |
| rkcanfd_ethtool_get_strings(struct net_device *ndev, u32 stringset, u8 *buf) | |
| { | |
| switch (stringset) { | |
| case ETH_SS_STATS: | |
| memcpy(buf, rkcanfd_stats_strings, | |
| sizeof(rkcanfd_stats_strings)); | |
| } | |
| } | |
| static int rkcanfd_ethtool_get_sset_count(struct net_device *netdev, int sset) | |
| { | |
| switch (sset) { | |
| case ETH_SS_STATS: | |
| return ARRAY_SIZE(rkcanfd_stats_strings); | |
| default: | |
| return -EOPNOTSUPP; | |
| } | |
| } | |
| static void | |
| rkcanfd_ethtool_get_ethtool_stats(struct net_device *ndev, | |
| struct ethtool_stats *stats, u64 *data) | |
| { | |
| struct rkcanfd_priv *priv = netdev_priv(ndev); | |
| struct rkcanfd_stats *rkcanfd_stats; | |
| unsigned int start; | |
| rkcanfd_stats = &priv->stats; | |
| do { | |
| start = u64_stats_fetch_begin(&rkcanfd_stats->syncp); | |
| data[RKCANFD_STATS_TYPE_RX_FIFO_EMPTY_ERRORS] = | |
| u64_stats_read(&rkcanfd_stats->rx_fifo_empty_errors); | |
| data[RKCANFD_STATS_TYPE_TX_EXTENDED_AS_STANDARD_ERRORS] = | |
| u64_stats_read(&rkcanfd_stats->tx_extended_as_standard_errors); | |
| } while (u64_stats_fetch_retry(&rkcanfd_stats->syncp, start)); | |
| } | |
| static const struct ethtool_ops rkcanfd_ethtool_ops = { | |
| .get_ts_info = can_ethtool_op_get_ts_info_hwts, | |
| .get_strings = rkcanfd_ethtool_get_strings, | |
| .get_sset_count = rkcanfd_ethtool_get_sset_count, | |
| .get_ethtool_stats = rkcanfd_ethtool_get_ethtool_stats, | |
| }; | |
| void rkcanfd_ethtool_init(struct rkcanfd_priv *priv) | |
| { | |
| priv->ndev->ethtool_ops = &rkcanfd_ethtool_ops; | |
| u64_stats_init(&priv->stats.syncp); | |
| } |