123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2016, The Linux Foundation. All rights reserved.
- */
- #include <linux/ethtool.h>
- #include <linux/phy.h>
- #include "emac.h"
- static const char * const emac_ethtool_stat_strings[] = {
- "rx_ok",
- "rx_bcast",
- "rx_mcast",
- "rx_pause",
- "rx_ctrl",
- "rx_fcs_err",
- "rx_len_err",
- "rx_byte_cnt",
- "rx_runt",
- "rx_frag",
- "rx_sz_64",
- "rx_sz_65_127",
- "rx_sz_128_255",
- "rx_sz_256_511",
- "rx_sz_512_1023",
- "rx_sz_1024_1518",
- "rx_sz_1519_max",
- "rx_sz_ov",
- "rx_rxf_ov",
- "rx_align_err",
- "rx_bcast_byte_cnt",
- "rx_mcast_byte_cnt",
- "rx_err_addr",
- "rx_crc_align",
- "rx_jabbers",
- "tx_ok",
- "tx_bcast",
- "tx_mcast",
- "tx_pause",
- "tx_exc_defer",
- "tx_ctrl",
- "tx_defer",
- "tx_byte_cnt",
- "tx_sz_64",
- "tx_sz_65_127",
- "tx_sz_128_255",
- "tx_sz_256_511",
- "tx_sz_512_1023",
- "tx_sz_1024_1518",
- "tx_sz_1519_max",
- "tx_1_col",
- "tx_2_col",
- "tx_late_col",
- "tx_abort_col",
- "tx_underrun",
- "tx_rd_eop",
- "tx_len_err",
- "tx_trunc",
- "tx_bcast_byte",
- "tx_mcast_byte",
- "tx_col",
- };
- #define EMAC_STATS_LEN ARRAY_SIZE(emac_ethtool_stat_strings)
- static u32 emac_get_msglevel(struct net_device *netdev)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- return adpt->msg_enable;
- }
- static void emac_set_msglevel(struct net_device *netdev, u32 data)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- adpt->msg_enable = data;
- }
- static int emac_get_sset_count(struct net_device *netdev, int sset)
- {
- switch (sset) {
- case ETH_SS_PRIV_FLAGS:
- return 1;
- case ETH_SS_STATS:
- return EMAC_STATS_LEN;
- default:
- return -EOPNOTSUPP;
- }
- }
- static void emac_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
- {
- unsigned int i;
- switch (stringset) {
- case ETH_SS_PRIV_FLAGS:
- strcpy(data, "single-pause-mode");
- break;
- case ETH_SS_STATS:
- for (i = 0; i < EMAC_STATS_LEN; i++) {
- strscpy(data, emac_ethtool_stat_strings[i],
- ETH_GSTRING_LEN);
- data += ETH_GSTRING_LEN;
- }
- break;
- }
- }
- static void emac_get_ethtool_stats(struct net_device *netdev,
- struct ethtool_stats *stats,
- u64 *data)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- spin_lock(&adpt->stats.lock);
- emac_update_hw_stats(adpt);
- memcpy(data, &adpt->stats, EMAC_STATS_LEN * sizeof(u64));
- spin_unlock(&adpt->stats.lock);
- }
- static int emac_nway_reset(struct net_device *netdev)
- {
- struct phy_device *phydev = netdev->phydev;
- if (!phydev)
- return -ENODEV;
- return genphy_restart_aneg(phydev);
- }
- static void emac_get_ringparam(struct net_device *netdev,
- struct ethtool_ringparam *ring,
- struct kernel_ethtool_ringparam *kernel_ring,
- struct netlink_ext_ack *extack)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- ring->rx_max_pending = EMAC_MAX_RX_DESCS;
- ring->tx_max_pending = EMAC_MAX_TX_DESCS;
- ring->rx_pending = adpt->rx_desc_cnt;
- ring->tx_pending = adpt->tx_desc_cnt;
- }
- static int emac_set_ringparam(struct net_device *netdev,
- struct ethtool_ringparam *ring,
- struct kernel_ethtool_ringparam *kernel_ring,
- struct netlink_ext_ack *extack)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- /* We don't have separate queues/rings for small/large frames, so
- * reject any attempt to specify those values separately.
- */
- if (ring->rx_mini_pending || ring->rx_jumbo_pending)
- return -EINVAL;
- adpt->tx_desc_cnt =
- clamp_val(ring->tx_pending, EMAC_MIN_TX_DESCS, EMAC_MAX_TX_DESCS);
- adpt->rx_desc_cnt =
- clamp_val(ring->rx_pending, EMAC_MIN_RX_DESCS, EMAC_MAX_RX_DESCS);
- if (netif_running(netdev))
- return emac_reinit_locked(adpt);
- return 0;
- }
- static void emac_get_pauseparam(struct net_device *netdev,
- struct ethtool_pauseparam *pause)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- pause->autoneg = adpt->automatic ? AUTONEG_ENABLE : AUTONEG_DISABLE;
- pause->rx_pause = adpt->rx_flow_control ? 1 : 0;
- pause->tx_pause = adpt->tx_flow_control ? 1 : 0;
- }
- static int emac_set_pauseparam(struct net_device *netdev,
- struct ethtool_pauseparam *pause)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- adpt->automatic = pause->autoneg == AUTONEG_ENABLE;
- adpt->rx_flow_control = pause->rx_pause != 0;
- adpt->tx_flow_control = pause->tx_pause != 0;
- if (netif_running(netdev))
- return emac_reinit_locked(adpt);
- return 0;
- }
- /* Selected registers that might want to track during runtime. */
- static const u16 emac_regs[] = {
- EMAC_DMA_MAS_CTRL,
- EMAC_MAC_CTRL,
- EMAC_TXQ_CTRL_0,
- EMAC_RXQ_CTRL_0,
- EMAC_DMA_CTRL,
- EMAC_INT_MASK,
- EMAC_AXI_MAST_CTRL,
- EMAC_CORE_HW_VERSION,
- EMAC_MISC_CTRL,
- };
- /* Every time emac_regs[] above is changed, increase this version number. */
- #define EMAC_REGS_VERSION 0
- #define EMAC_MAX_REG_SIZE ARRAY_SIZE(emac_regs)
- static void emac_get_regs(struct net_device *netdev,
- struct ethtool_regs *regs, void *buff)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- u32 *val = buff;
- unsigned int i;
- regs->version = EMAC_REGS_VERSION;
- regs->len = EMAC_MAX_REG_SIZE * sizeof(u32);
- for (i = 0; i < EMAC_MAX_REG_SIZE; i++)
- val[i] = readl(adpt->base + emac_regs[i]);
- }
- static int emac_get_regs_len(struct net_device *netdev)
- {
- return EMAC_MAX_REG_SIZE * sizeof(u32);
- }
- #define EMAC_PRIV_ENABLE_SINGLE_PAUSE BIT(0)
- static int emac_set_priv_flags(struct net_device *netdev, u32 flags)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- adpt->single_pause_mode = !!(flags & EMAC_PRIV_ENABLE_SINGLE_PAUSE);
- if (netif_running(netdev))
- return emac_reinit_locked(adpt);
- return 0;
- }
- static u32 emac_get_priv_flags(struct net_device *netdev)
- {
- struct emac_adapter *adpt = netdev_priv(netdev);
- return adpt->single_pause_mode ? EMAC_PRIV_ENABLE_SINGLE_PAUSE : 0;
- }
- static const struct ethtool_ops emac_ethtool_ops = {
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
- .get_msglevel = emac_get_msglevel,
- .set_msglevel = emac_set_msglevel,
- .get_sset_count = emac_get_sset_count,
- .get_strings = emac_get_strings,
- .get_ethtool_stats = emac_get_ethtool_stats,
- .get_ringparam = emac_get_ringparam,
- .set_ringparam = emac_set_ringparam,
- .get_pauseparam = emac_get_pauseparam,
- .set_pauseparam = emac_set_pauseparam,
- .nway_reset = emac_nway_reset,
- .get_link = ethtool_op_get_link,
- .get_regs_len = emac_get_regs_len,
- .get_regs = emac_get_regs,
- .set_priv_flags = emac_set_priv_flags,
- .get_priv_flags = emac_get_priv_flags,
- };
- void emac_set_ethtool_ops(struct net_device *netdev)
- {
- netdev->ethtool_ops = &emac_ethtool_ops;
- }
|