162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2016, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/ethtool.h> 662306a36Sopenharmony_ci#include <linux/phy.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "emac.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistatic const char * const emac_ethtool_stat_strings[] = { 1162306a36Sopenharmony_ci "rx_ok", 1262306a36Sopenharmony_ci "rx_bcast", 1362306a36Sopenharmony_ci "rx_mcast", 1462306a36Sopenharmony_ci "rx_pause", 1562306a36Sopenharmony_ci "rx_ctrl", 1662306a36Sopenharmony_ci "rx_fcs_err", 1762306a36Sopenharmony_ci "rx_len_err", 1862306a36Sopenharmony_ci "rx_byte_cnt", 1962306a36Sopenharmony_ci "rx_runt", 2062306a36Sopenharmony_ci "rx_frag", 2162306a36Sopenharmony_ci "rx_sz_64", 2262306a36Sopenharmony_ci "rx_sz_65_127", 2362306a36Sopenharmony_ci "rx_sz_128_255", 2462306a36Sopenharmony_ci "rx_sz_256_511", 2562306a36Sopenharmony_ci "rx_sz_512_1023", 2662306a36Sopenharmony_ci "rx_sz_1024_1518", 2762306a36Sopenharmony_ci "rx_sz_1519_max", 2862306a36Sopenharmony_ci "rx_sz_ov", 2962306a36Sopenharmony_ci "rx_rxf_ov", 3062306a36Sopenharmony_ci "rx_align_err", 3162306a36Sopenharmony_ci "rx_bcast_byte_cnt", 3262306a36Sopenharmony_ci "rx_mcast_byte_cnt", 3362306a36Sopenharmony_ci "rx_err_addr", 3462306a36Sopenharmony_ci "rx_crc_align", 3562306a36Sopenharmony_ci "rx_jabbers", 3662306a36Sopenharmony_ci "tx_ok", 3762306a36Sopenharmony_ci "tx_bcast", 3862306a36Sopenharmony_ci "tx_mcast", 3962306a36Sopenharmony_ci "tx_pause", 4062306a36Sopenharmony_ci "tx_exc_defer", 4162306a36Sopenharmony_ci "tx_ctrl", 4262306a36Sopenharmony_ci "tx_defer", 4362306a36Sopenharmony_ci "tx_byte_cnt", 4462306a36Sopenharmony_ci "tx_sz_64", 4562306a36Sopenharmony_ci "tx_sz_65_127", 4662306a36Sopenharmony_ci "tx_sz_128_255", 4762306a36Sopenharmony_ci "tx_sz_256_511", 4862306a36Sopenharmony_ci "tx_sz_512_1023", 4962306a36Sopenharmony_ci "tx_sz_1024_1518", 5062306a36Sopenharmony_ci "tx_sz_1519_max", 5162306a36Sopenharmony_ci "tx_1_col", 5262306a36Sopenharmony_ci "tx_2_col", 5362306a36Sopenharmony_ci "tx_late_col", 5462306a36Sopenharmony_ci "tx_abort_col", 5562306a36Sopenharmony_ci "tx_underrun", 5662306a36Sopenharmony_ci "tx_rd_eop", 5762306a36Sopenharmony_ci "tx_len_err", 5862306a36Sopenharmony_ci "tx_trunc", 5962306a36Sopenharmony_ci "tx_bcast_byte", 6062306a36Sopenharmony_ci "tx_mcast_byte", 6162306a36Sopenharmony_ci "tx_col", 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define EMAC_STATS_LEN ARRAY_SIZE(emac_ethtool_stat_strings) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic u32 emac_get_msglevel(struct net_device *netdev) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return adpt->msg_enable; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void emac_set_msglevel(struct net_device *netdev, u32 data) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci adpt->msg_enable = data; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int emac_get_sset_count(struct net_device *netdev, int sset) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci switch (sset) { 8362306a36Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 8462306a36Sopenharmony_ci return 1; 8562306a36Sopenharmony_ci case ETH_SS_STATS: 8662306a36Sopenharmony_ci return EMAC_STATS_LEN; 8762306a36Sopenharmony_ci default: 8862306a36Sopenharmony_ci return -EOPNOTSUPP; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void emac_get_strings(struct net_device *netdev, u32 stringset, u8 *data) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci unsigned int i; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci switch (stringset) { 9762306a36Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 9862306a36Sopenharmony_ci strcpy(data, "single-pause-mode"); 9962306a36Sopenharmony_ci break; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci case ETH_SS_STATS: 10262306a36Sopenharmony_ci for (i = 0; i < EMAC_STATS_LEN; i++) { 10362306a36Sopenharmony_ci strscpy(data, emac_ethtool_stat_strings[i], 10462306a36Sopenharmony_ci ETH_GSTRING_LEN); 10562306a36Sopenharmony_ci data += ETH_GSTRING_LEN; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void emac_get_ethtool_stats(struct net_device *netdev, 11262306a36Sopenharmony_ci struct ethtool_stats *stats, 11362306a36Sopenharmony_ci u64 *data) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci spin_lock(&adpt->stats.lock); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci emac_update_hw_stats(adpt); 12062306a36Sopenharmony_ci memcpy(data, &adpt->stats, EMAC_STATS_LEN * sizeof(u64)); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci spin_unlock(&adpt->stats.lock); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int emac_nway_reset(struct net_device *netdev) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct phy_device *phydev = netdev->phydev; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!phydev) 13062306a36Sopenharmony_ci return -ENODEV; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return genphy_restart_aneg(phydev); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void emac_get_ringparam(struct net_device *netdev, 13662306a36Sopenharmony_ci struct ethtool_ringparam *ring, 13762306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 13862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci ring->rx_max_pending = EMAC_MAX_RX_DESCS; 14362306a36Sopenharmony_ci ring->tx_max_pending = EMAC_MAX_TX_DESCS; 14462306a36Sopenharmony_ci ring->rx_pending = adpt->rx_desc_cnt; 14562306a36Sopenharmony_ci ring->tx_pending = adpt->tx_desc_cnt; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int emac_set_ringparam(struct net_device *netdev, 14962306a36Sopenharmony_ci struct ethtool_ringparam *ring, 15062306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 15162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* We don't have separate queues/rings for small/large frames, so 15662306a36Sopenharmony_ci * reject any attempt to specify those values separately. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if (ring->rx_mini_pending || ring->rx_jumbo_pending) 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci adpt->tx_desc_cnt = 16262306a36Sopenharmony_ci clamp_val(ring->tx_pending, EMAC_MIN_TX_DESCS, EMAC_MAX_TX_DESCS); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci adpt->rx_desc_cnt = 16562306a36Sopenharmony_ci clamp_val(ring->rx_pending, EMAC_MIN_RX_DESCS, EMAC_MAX_RX_DESCS); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (netif_running(netdev)) 16862306a36Sopenharmony_ci return emac_reinit_locked(adpt); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void emac_get_pauseparam(struct net_device *netdev, 17462306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci pause->autoneg = adpt->automatic ? AUTONEG_ENABLE : AUTONEG_DISABLE; 17962306a36Sopenharmony_ci pause->rx_pause = adpt->rx_flow_control ? 1 : 0; 18062306a36Sopenharmony_ci pause->tx_pause = adpt->tx_flow_control ? 1 : 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int emac_set_pauseparam(struct net_device *netdev, 18462306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci adpt->automatic = pause->autoneg == AUTONEG_ENABLE; 18962306a36Sopenharmony_ci adpt->rx_flow_control = pause->rx_pause != 0; 19062306a36Sopenharmony_ci adpt->tx_flow_control = pause->tx_pause != 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (netif_running(netdev)) 19362306a36Sopenharmony_ci return emac_reinit_locked(adpt); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* Selected registers that might want to track during runtime. */ 19962306a36Sopenharmony_cistatic const u16 emac_regs[] = { 20062306a36Sopenharmony_ci EMAC_DMA_MAS_CTRL, 20162306a36Sopenharmony_ci EMAC_MAC_CTRL, 20262306a36Sopenharmony_ci EMAC_TXQ_CTRL_0, 20362306a36Sopenharmony_ci EMAC_RXQ_CTRL_0, 20462306a36Sopenharmony_ci EMAC_DMA_CTRL, 20562306a36Sopenharmony_ci EMAC_INT_MASK, 20662306a36Sopenharmony_ci EMAC_AXI_MAST_CTRL, 20762306a36Sopenharmony_ci EMAC_CORE_HW_VERSION, 20862306a36Sopenharmony_ci EMAC_MISC_CTRL, 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* Every time emac_regs[] above is changed, increase this version number. */ 21262306a36Sopenharmony_ci#define EMAC_REGS_VERSION 0 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#define EMAC_MAX_REG_SIZE ARRAY_SIZE(emac_regs) 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void emac_get_regs(struct net_device *netdev, 21762306a36Sopenharmony_ci struct ethtool_regs *regs, void *buff) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 22062306a36Sopenharmony_ci u32 *val = buff; 22162306a36Sopenharmony_ci unsigned int i; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci regs->version = EMAC_REGS_VERSION; 22462306a36Sopenharmony_ci regs->len = EMAC_MAX_REG_SIZE * sizeof(u32); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (i = 0; i < EMAC_MAX_REG_SIZE; i++) 22762306a36Sopenharmony_ci val[i] = readl(adpt->base + emac_regs[i]); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int emac_get_regs_len(struct net_device *netdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci return EMAC_MAX_REG_SIZE * sizeof(u32); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define EMAC_PRIV_ENABLE_SINGLE_PAUSE BIT(0) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int emac_set_priv_flags(struct net_device *netdev, u32 flags) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci adpt->single_pause_mode = !!(flags & EMAC_PRIV_ENABLE_SINGLE_PAUSE); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (netif_running(netdev)) 24462306a36Sopenharmony_ci return emac_reinit_locked(adpt); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic u32 emac_get_priv_flags(struct net_device *netdev) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return adpt->single_pause_mode ? EMAC_PRIV_ENABLE_SINGLE_PAUSE : 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic const struct ethtool_ops emac_ethtool_ops = { 25762306a36Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 25862306a36Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci .get_msglevel = emac_get_msglevel, 26162306a36Sopenharmony_ci .set_msglevel = emac_set_msglevel, 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci .get_sset_count = emac_get_sset_count, 26462306a36Sopenharmony_ci .get_strings = emac_get_strings, 26562306a36Sopenharmony_ci .get_ethtool_stats = emac_get_ethtool_stats, 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci .get_ringparam = emac_get_ringparam, 26862306a36Sopenharmony_ci .set_ringparam = emac_set_ringparam, 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci .get_pauseparam = emac_get_pauseparam, 27162306a36Sopenharmony_ci .set_pauseparam = emac_set_pauseparam, 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci .nway_reset = emac_nway_reset, 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci .get_regs_len = emac_get_regs_len, 27862306a36Sopenharmony_ci .get_regs = emac_get_regs, 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci .set_priv_flags = emac_set_priv_flags, 28162306a36Sopenharmony_ci .get_priv_flags = emac_get_priv_flags, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_civoid emac_set_ethtool_ops(struct net_device *netdev) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci netdev->ethtool_ops = &emac_ethtool_ops; 28762306a36Sopenharmony_ci} 288