18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2016, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 68c2ecf20Sopenharmony_ci#include <linux/phy.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "emac.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic const char * const emac_ethtool_stat_strings[] = { 118c2ecf20Sopenharmony_ci "rx_ok", 128c2ecf20Sopenharmony_ci "rx_bcast", 138c2ecf20Sopenharmony_ci "rx_mcast", 148c2ecf20Sopenharmony_ci "rx_pause", 158c2ecf20Sopenharmony_ci "rx_ctrl", 168c2ecf20Sopenharmony_ci "rx_fcs_err", 178c2ecf20Sopenharmony_ci "rx_len_err", 188c2ecf20Sopenharmony_ci "rx_byte_cnt", 198c2ecf20Sopenharmony_ci "rx_runt", 208c2ecf20Sopenharmony_ci "rx_frag", 218c2ecf20Sopenharmony_ci "rx_sz_64", 228c2ecf20Sopenharmony_ci "rx_sz_65_127", 238c2ecf20Sopenharmony_ci "rx_sz_128_255", 248c2ecf20Sopenharmony_ci "rx_sz_256_511", 258c2ecf20Sopenharmony_ci "rx_sz_512_1023", 268c2ecf20Sopenharmony_ci "rx_sz_1024_1518", 278c2ecf20Sopenharmony_ci "rx_sz_1519_max", 288c2ecf20Sopenharmony_ci "rx_sz_ov", 298c2ecf20Sopenharmony_ci "rx_rxf_ov", 308c2ecf20Sopenharmony_ci "rx_align_err", 318c2ecf20Sopenharmony_ci "rx_bcast_byte_cnt", 328c2ecf20Sopenharmony_ci "rx_mcast_byte_cnt", 338c2ecf20Sopenharmony_ci "rx_err_addr", 348c2ecf20Sopenharmony_ci "rx_crc_align", 358c2ecf20Sopenharmony_ci "rx_jabbers", 368c2ecf20Sopenharmony_ci "tx_ok", 378c2ecf20Sopenharmony_ci "tx_bcast", 388c2ecf20Sopenharmony_ci "tx_mcast", 398c2ecf20Sopenharmony_ci "tx_pause", 408c2ecf20Sopenharmony_ci "tx_exc_defer", 418c2ecf20Sopenharmony_ci "tx_ctrl", 428c2ecf20Sopenharmony_ci "tx_defer", 438c2ecf20Sopenharmony_ci "tx_byte_cnt", 448c2ecf20Sopenharmony_ci "tx_sz_64", 458c2ecf20Sopenharmony_ci "tx_sz_65_127", 468c2ecf20Sopenharmony_ci "tx_sz_128_255", 478c2ecf20Sopenharmony_ci "tx_sz_256_511", 488c2ecf20Sopenharmony_ci "tx_sz_512_1023", 498c2ecf20Sopenharmony_ci "tx_sz_1024_1518", 508c2ecf20Sopenharmony_ci "tx_sz_1519_max", 518c2ecf20Sopenharmony_ci "tx_1_col", 528c2ecf20Sopenharmony_ci "tx_2_col", 538c2ecf20Sopenharmony_ci "tx_late_col", 548c2ecf20Sopenharmony_ci "tx_abort_col", 558c2ecf20Sopenharmony_ci "tx_underrun", 568c2ecf20Sopenharmony_ci "tx_rd_eop", 578c2ecf20Sopenharmony_ci "tx_len_err", 588c2ecf20Sopenharmony_ci "tx_trunc", 598c2ecf20Sopenharmony_ci "tx_bcast_byte", 608c2ecf20Sopenharmony_ci "tx_mcast_byte", 618c2ecf20Sopenharmony_ci "tx_col", 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define EMAC_STATS_LEN ARRAY_SIZE(emac_ethtool_stat_strings) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic u32 emac_get_msglevel(struct net_device *netdev) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return adpt->msg_enable; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void emac_set_msglevel(struct net_device *netdev, u32 data) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci adpt->msg_enable = data; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int emac_get_sset_count(struct net_device *netdev, int sset) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci switch (sset) { 838c2ecf20Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 848c2ecf20Sopenharmony_ci return 1; 858c2ecf20Sopenharmony_ci case ETH_SS_STATS: 868c2ecf20Sopenharmony_ci return EMAC_STATS_LEN; 878c2ecf20Sopenharmony_ci default: 888c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void emac_get_strings(struct net_device *netdev, u32 stringset, u8 *data) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci unsigned int i; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci switch (stringset) { 978c2ecf20Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 988c2ecf20Sopenharmony_ci strcpy(data, "single-pause-mode"); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci case ETH_SS_STATS: 1028c2ecf20Sopenharmony_ci for (i = 0; i < EMAC_STATS_LEN; i++) { 1038c2ecf20Sopenharmony_ci strlcpy(data, emac_ethtool_stat_strings[i], 1048c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 1058c2ecf20Sopenharmony_ci data += ETH_GSTRING_LEN; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void emac_get_ethtool_stats(struct net_device *netdev, 1128c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 1138c2ecf20Sopenharmony_ci u64 *data) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_lock(&adpt->stats.lock); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci emac_update_hw_stats(adpt); 1208c2ecf20Sopenharmony_ci memcpy(data, &adpt->stats, EMAC_STATS_LEN * sizeof(u64)); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci spin_unlock(&adpt->stats.lock); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int emac_nway_reset(struct net_device *netdev) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct phy_device *phydev = netdev->phydev; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!phydev) 1308c2ecf20Sopenharmony_ci return -ENODEV; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return genphy_restart_aneg(phydev); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void emac_get_ringparam(struct net_device *netdev, 1368c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci ring->rx_max_pending = EMAC_MAX_RX_DESCS; 1418c2ecf20Sopenharmony_ci ring->tx_max_pending = EMAC_MAX_TX_DESCS; 1428c2ecf20Sopenharmony_ci ring->rx_pending = adpt->rx_desc_cnt; 1438c2ecf20Sopenharmony_ci ring->tx_pending = adpt->tx_desc_cnt; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int emac_set_ringparam(struct net_device *netdev, 1478c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* We don't have separate queues/rings for small/large frames, so 1528c2ecf20Sopenharmony_ci * reject any attempt to specify those values separately. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (ring->rx_mini_pending || ring->rx_jumbo_pending) 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci adpt->tx_desc_cnt = 1588c2ecf20Sopenharmony_ci clamp_val(ring->tx_pending, EMAC_MIN_TX_DESCS, EMAC_MAX_TX_DESCS); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci adpt->rx_desc_cnt = 1618c2ecf20Sopenharmony_ci clamp_val(ring->rx_pending, EMAC_MIN_RX_DESCS, EMAC_MAX_RX_DESCS); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (netif_running(netdev)) 1648c2ecf20Sopenharmony_ci return emac_reinit_locked(adpt); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void emac_get_pauseparam(struct net_device *netdev, 1708c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci pause->autoneg = adpt->automatic ? AUTONEG_ENABLE : AUTONEG_DISABLE; 1758c2ecf20Sopenharmony_ci pause->rx_pause = adpt->rx_flow_control ? 1 : 0; 1768c2ecf20Sopenharmony_ci pause->tx_pause = adpt->tx_flow_control ? 1 : 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int emac_set_pauseparam(struct net_device *netdev, 1808c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pause) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci adpt->automatic = pause->autoneg == AUTONEG_ENABLE; 1858c2ecf20Sopenharmony_ci adpt->rx_flow_control = pause->rx_pause != 0; 1868c2ecf20Sopenharmony_ci adpt->tx_flow_control = pause->tx_pause != 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (netif_running(netdev)) 1898c2ecf20Sopenharmony_ci return emac_reinit_locked(adpt); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* Selected registers that might want to track during runtime. */ 1958c2ecf20Sopenharmony_cistatic const u16 emac_regs[] = { 1968c2ecf20Sopenharmony_ci EMAC_DMA_MAS_CTRL, 1978c2ecf20Sopenharmony_ci EMAC_MAC_CTRL, 1988c2ecf20Sopenharmony_ci EMAC_TXQ_CTRL_0, 1998c2ecf20Sopenharmony_ci EMAC_RXQ_CTRL_0, 2008c2ecf20Sopenharmony_ci EMAC_DMA_CTRL, 2018c2ecf20Sopenharmony_ci EMAC_INT_MASK, 2028c2ecf20Sopenharmony_ci EMAC_AXI_MAST_CTRL, 2038c2ecf20Sopenharmony_ci EMAC_CORE_HW_VERSION, 2048c2ecf20Sopenharmony_ci EMAC_MISC_CTRL, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* Every time emac_regs[] above is changed, increase this version number. */ 2088c2ecf20Sopenharmony_ci#define EMAC_REGS_VERSION 0 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci#define EMAC_MAX_REG_SIZE ARRAY_SIZE(emac_regs) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void emac_get_regs(struct net_device *netdev, 2138c2ecf20Sopenharmony_ci struct ethtool_regs *regs, void *buff) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2168c2ecf20Sopenharmony_ci u32 *val = buff; 2178c2ecf20Sopenharmony_ci unsigned int i; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci regs->version = EMAC_REGS_VERSION; 2208c2ecf20Sopenharmony_ci regs->len = EMAC_MAX_REG_SIZE * sizeof(u32); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (i = 0; i < EMAC_MAX_REG_SIZE; i++) 2238c2ecf20Sopenharmony_ci val[i] = readl(adpt->base + emac_regs[i]); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int emac_get_regs_len(struct net_device *netdev) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci return EMAC_MAX_REG_SIZE * sizeof(u32); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define EMAC_PRIV_ENABLE_SINGLE_PAUSE BIT(0) 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int emac_set_priv_flags(struct net_device *netdev, u32 flags) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci adpt->single_pause_mode = !!(flags & EMAC_PRIV_ENABLE_SINGLE_PAUSE); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (netif_running(netdev)) 2408c2ecf20Sopenharmony_ci return emac_reinit_locked(adpt); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic u32 emac_get_priv_flags(struct net_device *netdev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct emac_adapter *adpt = netdev_priv(netdev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return adpt->single_pause_mode ? EMAC_PRIV_ENABLE_SINGLE_PAUSE : 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic const struct ethtool_ops emac_ethtool_ops = { 2538c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 2548c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci .get_msglevel = emac_get_msglevel, 2578c2ecf20Sopenharmony_ci .set_msglevel = emac_set_msglevel, 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci .get_sset_count = emac_get_sset_count, 2608c2ecf20Sopenharmony_ci .get_strings = emac_get_strings, 2618c2ecf20Sopenharmony_ci .get_ethtool_stats = emac_get_ethtool_stats, 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci .get_ringparam = emac_get_ringparam, 2648c2ecf20Sopenharmony_ci .set_ringparam = emac_set_ringparam, 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci .get_pauseparam = emac_get_pauseparam, 2678c2ecf20Sopenharmony_ci .set_pauseparam = emac_set_pauseparam, 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci .nway_reset = emac_nway_reset, 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci .get_regs_len = emac_get_regs_len, 2748c2ecf20Sopenharmony_ci .get_regs = emac_get_regs, 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci .set_priv_flags = emac_set_priv_flags, 2778c2ecf20Sopenharmony_ci .get_priv_flags = emac_get_priv_flags, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_civoid emac_set_ethtool_ops(struct net_device *netdev) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci netdev->ethtool_ops = &emac_ethtool_ops; 2838c2ecf20Sopenharmony_ci} 284