162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2007 Mellanox Technologies. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/kernel.h> 3562306a36Sopenharmony_ci#include <linux/ethtool.h> 3662306a36Sopenharmony_ci#include <linux/netdevice.h> 3762306a36Sopenharmony_ci#include <linux/mlx4/driver.h> 3862306a36Sopenharmony_ci#include <linux/mlx4/device.h> 3962306a36Sopenharmony_ci#include <linux/in.h> 4062306a36Sopenharmony_ci#include <net/ip.h> 4162306a36Sopenharmony_ci#include <linux/bitmap.h> 4262306a36Sopenharmony_ci#include <linux/mii.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include "mlx4_en.h" 4562306a36Sopenharmony_ci#include "en_port.h" 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define EN_ETHTOOL_QP_ATTACH (1ull << 63) 4862306a36Sopenharmony_ci#define EN_ETHTOOL_SHORT_MASK cpu_to_be16(0xffff) 4962306a36Sopenharmony_ci#define EN_ETHTOOL_WORD_MASK cpu_to_be32(0xffffffff) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciint mlx4_en_moderation_update(struct mlx4_en_priv *priv) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci int i, t; 5462306a36Sopenharmony_ci int err = 0; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (t = 0 ; t < MLX4_EN_NUM_TX_TYPES; t++) { 5762306a36Sopenharmony_ci for (i = 0; i < priv->tx_ring_num[t]; i++) { 5862306a36Sopenharmony_ci priv->tx_cq[t][i]->moder_cnt = priv->tx_frames; 5962306a36Sopenharmony_ci priv->tx_cq[t][i]->moder_time = priv->tx_usecs; 6062306a36Sopenharmony_ci if (priv->port_up) { 6162306a36Sopenharmony_ci err = mlx4_en_set_cq_moder(priv, 6262306a36Sopenharmony_ci priv->tx_cq[t][i]); 6362306a36Sopenharmony_ci if (err) 6462306a36Sopenharmony_ci return err; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (priv->adaptive_rx_coal) 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < priv->rx_ring_num; i++) { 7362306a36Sopenharmony_ci priv->rx_cq[i]->moder_cnt = priv->rx_frames; 7462306a36Sopenharmony_ci priv->rx_cq[i]->moder_time = priv->rx_usecs; 7562306a36Sopenharmony_ci priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; 7662306a36Sopenharmony_ci if (priv->port_up) { 7762306a36Sopenharmony_ci err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]); 7862306a36Sopenharmony_ci if (err) 7962306a36Sopenharmony_ci return err; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return err; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void 8762306a36Sopenharmony_cimlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 9062306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci strscpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); 9362306a36Sopenharmony_ci strscpy(drvinfo->version, DRV_VERSION, 9462306a36Sopenharmony_ci sizeof(drvinfo->version)); 9562306a36Sopenharmony_ci snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), 9662306a36Sopenharmony_ci "%d.%d.%d", 9762306a36Sopenharmony_ci (u16) (mdev->dev->caps.fw_ver >> 32), 9862306a36Sopenharmony_ci (u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff), 9962306a36Sopenharmony_ci (u16) (mdev->dev->caps.fw_ver & 0xffff)); 10062306a36Sopenharmony_ci strscpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev), 10162306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic const char mlx4_en_priv_flags[][ETH_GSTRING_LEN] = { 10562306a36Sopenharmony_ci "blueflame", 10662306a36Sopenharmony_ci "phv-bit" 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const char main_strings[][ETH_GSTRING_LEN] = { 11062306a36Sopenharmony_ci /* main statistics */ 11162306a36Sopenharmony_ci "rx_packets", "tx_packets", "rx_bytes", "tx_bytes", "rx_errors", 11262306a36Sopenharmony_ci "tx_errors", "rx_dropped", "tx_dropped", "multicast", "collisions", 11362306a36Sopenharmony_ci "rx_length_errors", "rx_over_errors", "rx_crc_errors", 11462306a36Sopenharmony_ci "rx_frame_errors", "rx_fifo_errors", "rx_missed_errors", 11562306a36Sopenharmony_ci "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", 11662306a36Sopenharmony_ci "tx_heartbeat_errors", "tx_window_errors", 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* port statistics */ 11962306a36Sopenharmony_ci "tso_packets", 12062306a36Sopenharmony_ci "xmit_more", 12162306a36Sopenharmony_ci "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_pages", 12262306a36Sopenharmony_ci "rx_csum_good", "rx_csum_none", "rx_csum_complete", "tx_chksum_offload", 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* pf statistics */ 12562306a36Sopenharmony_ci "pf_rx_packets", 12662306a36Sopenharmony_ci "pf_rx_bytes", 12762306a36Sopenharmony_ci "pf_tx_packets", 12862306a36Sopenharmony_ci "pf_tx_bytes", 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* priority flow control statistics rx */ 13162306a36Sopenharmony_ci "rx_pause_prio_0", "rx_pause_duration_prio_0", 13262306a36Sopenharmony_ci "rx_pause_transition_prio_0", 13362306a36Sopenharmony_ci "rx_pause_prio_1", "rx_pause_duration_prio_1", 13462306a36Sopenharmony_ci "rx_pause_transition_prio_1", 13562306a36Sopenharmony_ci "rx_pause_prio_2", "rx_pause_duration_prio_2", 13662306a36Sopenharmony_ci "rx_pause_transition_prio_2", 13762306a36Sopenharmony_ci "rx_pause_prio_3", "rx_pause_duration_prio_3", 13862306a36Sopenharmony_ci "rx_pause_transition_prio_3", 13962306a36Sopenharmony_ci "rx_pause_prio_4", "rx_pause_duration_prio_4", 14062306a36Sopenharmony_ci "rx_pause_transition_prio_4", 14162306a36Sopenharmony_ci "rx_pause_prio_5", "rx_pause_duration_prio_5", 14262306a36Sopenharmony_ci "rx_pause_transition_prio_5", 14362306a36Sopenharmony_ci "rx_pause_prio_6", "rx_pause_duration_prio_6", 14462306a36Sopenharmony_ci "rx_pause_transition_prio_6", 14562306a36Sopenharmony_ci "rx_pause_prio_7", "rx_pause_duration_prio_7", 14662306a36Sopenharmony_ci "rx_pause_transition_prio_7", 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* flow control statistics rx */ 14962306a36Sopenharmony_ci "rx_pause", "rx_pause_duration", "rx_pause_transition", 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* priority flow control statistics tx */ 15262306a36Sopenharmony_ci "tx_pause_prio_0", "tx_pause_duration_prio_0", 15362306a36Sopenharmony_ci "tx_pause_transition_prio_0", 15462306a36Sopenharmony_ci "tx_pause_prio_1", "tx_pause_duration_prio_1", 15562306a36Sopenharmony_ci "tx_pause_transition_prio_1", 15662306a36Sopenharmony_ci "tx_pause_prio_2", "tx_pause_duration_prio_2", 15762306a36Sopenharmony_ci "tx_pause_transition_prio_2", 15862306a36Sopenharmony_ci "tx_pause_prio_3", "tx_pause_duration_prio_3", 15962306a36Sopenharmony_ci "tx_pause_transition_prio_3", 16062306a36Sopenharmony_ci "tx_pause_prio_4", "tx_pause_duration_prio_4", 16162306a36Sopenharmony_ci "tx_pause_transition_prio_4", 16262306a36Sopenharmony_ci "tx_pause_prio_5", "tx_pause_duration_prio_5", 16362306a36Sopenharmony_ci "tx_pause_transition_prio_5", 16462306a36Sopenharmony_ci "tx_pause_prio_6", "tx_pause_duration_prio_6", 16562306a36Sopenharmony_ci "tx_pause_transition_prio_6", 16662306a36Sopenharmony_ci "tx_pause_prio_7", "tx_pause_duration_prio_7", 16762306a36Sopenharmony_ci "tx_pause_transition_prio_7", 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* flow control statistics tx */ 17062306a36Sopenharmony_ci "tx_pause", "tx_pause_duration", "tx_pause_transition", 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* packet statistics */ 17362306a36Sopenharmony_ci "rx_multicast_packets", 17462306a36Sopenharmony_ci "rx_broadcast_packets", 17562306a36Sopenharmony_ci "rx_jabbers", 17662306a36Sopenharmony_ci "rx_in_range_length_error", 17762306a36Sopenharmony_ci "rx_out_range_length_error", 17862306a36Sopenharmony_ci "tx_multicast_packets", 17962306a36Sopenharmony_ci "tx_broadcast_packets", 18062306a36Sopenharmony_ci "rx_prio_0_packets", "rx_prio_0_bytes", 18162306a36Sopenharmony_ci "rx_prio_1_packets", "rx_prio_1_bytes", 18262306a36Sopenharmony_ci "rx_prio_2_packets", "rx_prio_2_bytes", 18362306a36Sopenharmony_ci "rx_prio_3_packets", "rx_prio_3_bytes", 18462306a36Sopenharmony_ci "rx_prio_4_packets", "rx_prio_4_bytes", 18562306a36Sopenharmony_ci "rx_prio_5_packets", "rx_prio_5_bytes", 18662306a36Sopenharmony_ci "rx_prio_6_packets", "rx_prio_6_bytes", 18762306a36Sopenharmony_ci "rx_prio_7_packets", "rx_prio_7_bytes", 18862306a36Sopenharmony_ci "rx_novlan_packets", "rx_novlan_bytes", 18962306a36Sopenharmony_ci "tx_prio_0_packets", "tx_prio_0_bytes", 19062306a36Sopenharmony_ci "tx_prio_1_packets", "tx_prio_1_bytes", 19162306a36Sopenharmony_ci "tx_prio_2_packets", "tx_prio_2_bytes", 19262306a36Sopenharmony_ci "tx_prio_3_packets", "tx_prio_3_bytes", 19362306a36Sopenharmony_ci "tx_prio_4_packets", "tx_prio_4_bytes", 19462306a36Sopenharmony_ci "tx_prio_5_packets", "tx_prio_5_bytes", 19562306a36Sopenharmony_ci "tx_prio_6_packets", "tx_prio_6_bytes", 19662306a36Sopenharmony_ci "tx_prio_7_packets", "tx_prio_7_bytes", 19762306a36Sopenharmony_ci "tx_novlan_packets", "tx_novlan_bytes", 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* xdp statistics */ 20062306a36Sopenharmony_ci "rx_xdp_drop", 20162306a36Sopenharmony_ci "rx_xdp_redirect", 20262306a36Sopenharmony_ci "rx_xdp_redirect_fail", 20362306a36Sopenharmony_ci "rx_xdp_tx", 20462306a36Sopenharmony_ci "rx_xdp_tx_full", 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* phy statistics */ 20762306a36Sopenharmony_ci "rx_packets_phy", "rx_bytes_phy", 20862306a36Sopenharmony_ci "tx_packets_phy", "tx_bytes_phy", 20962306a36Sopenharmony_ci}; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic const char mlx4_en_test_names[][ETH_GSTRING_LEN]= { 21262306a36Sopenharmony_ci "Interrupt Test", 21362306a36Sopenharmony_ci "Link Test", 21462306a36Sopenharmony_ci "Speed Test", 21562306a36Sopenharmony_ci "Register Test", 21662306a36Sopenharmony_ci "Loopback Test", 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic u32 mlx4_en_get_msglevel(struct net_device *dev) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void mlx4_en_set_msglevel(struct net_device *dev, u32 val) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci ((struct mlx4_en_priv *) netdev_priv(dev))->msg_enable = val; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void mlx4_en_get_wol(struct net_device *netdev, 23062306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(netdev); 23362306a36Sopenharmony_ci struct mlx4_caps *caps = &priv->mdev->dev->caps; 23462306a36Sopenharmony_ci int err = 0; 23562306a36Sopenharmony_ci u64 config = 0; 23662306a36Sopenharmony_ci u64 mask; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if ((priv->port < 1) || (priv->port > 2)) { 23962306a36Sopenharmony_ci en_err(priv, "Failed to get WoL information\n"); 24062306a36Sopenharmony_ci return; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : 24462306a36Sopenharmony_ci MLX4_DEV_CAP_FLAG_WOL_PORT2; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!(caps->flags & mask)) { 24762306a36Sopenharmony_ci wol->supported = 0; 24862306a36Sopenharmony_ci wol->wolopts = 0; 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (caps->wol_port[priv->port]) 25362306a36Sopenharmony_ci wol->supported = WAKE_MAGIC; 25462306a36Sopenharmony_ci else 25562306a36Sopenharmony_ci wol->supported = 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); 25862306a36Sopenharmony_ci if (err) { 25962306a36Sopenharmony_ci en_err(priv, "Failed to get WoL information\n"); 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if ((config & MLX4_EN_WOL_ENABLED) && (config & MLX4_EN_WOL_MAGIC)) 26462306a36Sopenharmony_ci wol->wolopts = WAKE_MAGIC; 26562306a36Sopenharmony_ci else 26662306a36Sopenharmony_ci wol->wolopts = 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int mlx4_en_set_wol(struct net_device *netdev, 27062306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(netdev); 27362306a36Sopenharmony_ci u64 config = 0; 27462306a36Sopenharmony_ci int err = 0; 27562306a36Sopenharmony_ci u64 mask; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if ((priv->port < 1) || (priv->port > 2)) 27862306a36Sopenharmony_ci return -EOPNOTSUPP; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : 28162306a36Sopenharmony_ci MLX4_DEV_CAP_FLAG_WOL_PORT2; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!(priv->mdev->dev->caps.flags & mask)) 28462306a36Sopenharmony_ci return -EOPNOTSUPP; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (wol->supported & ~WAKE_MAGIC) 28762306a36Sopenharmony_ci return -EINVAL; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); 29062306a36Sopenharmony_ci if (err) { 29162306a36Sopenharmony_ci en_err(priv, "Failed to get WoL info, unable to modify\n"); 29262306a36Sopenharmony_ci return err; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 29662306a36Sopenharmony_ci config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED | 29762306a36Sopenharmony_ci MLX4_EN_WOL_MAGIC; 29862306a36Sopenharmony_ci } else { 29962306a36Sopenharmony_ci config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC); 30062306a36Sopenharmony_ci config |= MLX4_EN_WOL_DO_MODIFY; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci err = mlx4_wol_write(priv->mdev->dev, config, priv->port); 30462306a36Sopenharmony_ci if (err) 30562306a36Sopenharmony_ci en_err(priv, "Failed to set WoL information\n"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistruct bitmap_iterator { 31162306a36Sopenharmony_ci unsigned long *stats_bitmap; 31262306a36Sopenharmony_ci unsigned int count; 31362306a36Sopenharmony_ci unsigned int iterator; 31462306a36Sopenharmony_ci bool advance_array; /* if set, force no increments */ 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic inline void bitmap_iterator_init(struct bitmap_iterator *h, 31862306a36Sopenharmony_ci unsigned long *stats_bitmap, 31962306a36Sopenharmony_ci int count) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci h->iterator = 0; 32262306a36Sopenharmony_ci h->advance_array = !bitmap_empty(stats_bitmap, count); 32362306a36Sopenharmony_ci h->count = h->advance_array ? bitmap_weight(stats_bitmap, count) 32462306a36Sopenharmony_ci : count; 32562306a36Sopenharmony_ci h->stats_bitmap = stats_bitmap; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic inline int bitmap_iterator_test(struct bitmap_iterator *h) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci return !h->advance_array ? 1 : test_bit(h->iterator, h->stats_bitmap); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic inline int bitmap_iterator_inc(struct bitmap_iterator *h) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci return h->iterator++; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic inline unsigned int 33962306a36Sopenharmony_cibitmap_iterator_count(struct bitmap_iterator *h) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return h->count; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int mlx4_en_get_sset_count(struct net_device *dev, int sset) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 34762306a36Sopenharmony_ci struct bitmap_iterator it; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (sset) { 35262306a36Sopenharmony_ci case ETH_SS_STATS: 35362306a36Sopenharmony_ci return bitmap_iterator_count(&it) + 35462306a36Sopenharmony_ci (priv->tx_ring_num[TX] * 2) + 35562306a36Sopenharmony_ci (priv->rx_ring_num * (3 + NUM_XDP_STATS)); 35662306a36Sopenharmony_ci case ETH_SS_TEST: 35762306a36Sopenharmony_ci return MLX4_EN_NUM_SELF_TEST - !(priv->mdev->dev->caps.flags 35862306a36Sopenharmony_ci & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) * 2; 35962306a36Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 36062306a36Sopenharmony_ci return ARRAY_SIZE(mlx4_en_priv_flags); 36162306a36Sopenharmony_ci default: 36262306a36Sopenharmony_ci return -EOPNOTSUPP; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void mlx4_en_get_ethtool_stats(struct net_device *dev, 36762306a36Sopenharmony_ci struct ethtool_stats *stats, uint64_t *data) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 37062306a36Sopenharmony_ci int index = 0; 37162306a36Sopenharmony_ci int i; 37262306a36Sopenharmony_ci struct bitmap_iterator it; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci spin_lock_bh(&priv->stats_lock); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci mlx4_en_fold_software_stats(dev); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci for (i = 0; i < NUM_MAIN_STATS; i++, bitmap_iterator_inc(&it)) 38162306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 38262306a36Sopenharmony_ci data[index++] = ((unsigned long *)&dev->stats)[i]; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci for (i = 0; i < NUM_PORT_STATS; i++, bitmap_iterator_inc(&it)) 38562306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 38662306a36Sopenharmony_ci data[index++] = ((unsigned long *)&priv->port_stats)[i]; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for (i = 0; i < NUM_PF_STATS; i++, bitmap_iterator_inc(&it)) 38962306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 39062306a36Sopenharmony_ci data[index++] = 39162306a36Sopenharmony_ci ((unsigned long *)&priv->pf_stats)[i]; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (i = 0; i < NUM_FLOW_PRIORITY_STATS_RX; 39462306a36Sopenharmony_ci i++, bitmap_iterator_inc(&it)) 39562306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 39662306a36Sopenharmony_ci data[index++] = 39762306a36Sopenharmony_ci ((u64 *)&priv->rx_priority_flowstats)[i]; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci for (i = 0; i < NUM_FLOW_STATS_RX; i++, bitmap_iterator_inc(&it)) 40062306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 40162306a36Sopenharmony_ci data[index++] = ((u64 *)&priv->rx_flowstats)[i]; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci for (i = 0; i < NUM_FLOW_PRIORITY_STATS_TX; 40462306a36Sopenharmony_ci i++, bitmap_iterator_inc(&it)) 40562306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 40662306a36Sopenharmony_ci data[index++] = 40762306a36Sopenharmony_ci ((u64 *)&priv->tx_priority_flowstats)[i]; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci for (i = 0; i < NUM_FLOW_STATS_TX; i++, bitmap_iterator_inc(&it)) 41062306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 41162306a36Sopenharmony_ci data[index++] = ((u64 *)&priv->tx_flowstats)[i]; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci for (i = 0; i < NUM_PKT_STATS; i++, bitmap_iterator_inc(&it)) 41462306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 41562306a36Sopenharmony_ci data[index++] = ((unsigned long *)&priv->pkstats)[i]; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci for (i = 0; i < NUM_XDP_STATS; i++, bitmap_iterator_inc(&it)) 41862306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 41962306a36Sopenharmony_ci data[index++] = ((unsigned long *)&priv->xdp_stats)[i]; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci for (i = 0; i < NUM_PHY_STATS; i++, bitmap_iterator_inc(&it)) 42262306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 42362306a36Sopenharmony_ci data[index++] = ((unsigned long *)&priv->phy_stats)[i]; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (i = 0; i < priv->tx_ring_num[TX]; i++) { 42662306a36Sopenharmony_ci data[index++] = priv->tx_ring[TX][i]->packets; 42762306a36Sopenharmony_ci data[index++] = priv->tx_ring[TX][i]->bytes; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci for (i = 0; i < priv->rx_ring_num; i++) { 43062306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->packets; 43162306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->bytes; 43262306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->dropped; 43362306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->xdp_drop; 43462306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->xdp_redirect; 43562306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->xdp_redirect_fail; 43662306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->xdp_tx; 43762306a36Sopenharmony_ci data[index++] = priv->rx_ring[i]->xdp_tx_full; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci spin_unlock_bh(&priv->stats_lock); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic void mlx4_en_self_test(struct net_device *dev, 44462306a36Sopenharmony_ci struct ethtool_test *etest, u64 *buf) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci mlx4_en_ex_selftest(dev, &etest->flags, buf); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void mlx4_en_get_strings(struct net_device *dev, 45062306a36Sopenharmony_ci uint32_t stringset, uint8_t *data) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 45362306a36Sopenharmony_ci int index = 0; 45462306a36Sopenharmony_ci int i, strings = 0; 45562306a36Sopenharmony_ci struct bitmap_iterator it; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci switch (stringset) { 46062306a36Sopenharmony_ci case ETH_SS_TEST: 46162306a36Sopenharmony_ci for (i = 0; i < MLX4_EN_NUM_SELF_TEST - 2; i++) 46262306a36Sopenharmony_ci strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); 46362306a36Sopenharmony_ci if (priv->mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_UC_LOOPBACK) 46462306a36Sopenharmony_ci for (; i < MLX4_EN_NUM_SELF_TEST; i++) 46562306a36Sopenharmony_ci strcpy(data + i * ETH_GSTRING_LEN, mlx4_en_test_names[i]); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci case ETH_SS_STATS: 46962306a36Sopenharmony_ci /* Add main counters */ 47062306a36Sopenharmony_ci for (i = 0; i < NUM_MAIN_STATS; i++, strings++, 47162306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 47262306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 47362306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 47462306a36Sopenharmony_ci main_strings[strings]); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci for (i = 0; i < NUM_PORT_STATS; i++, strings++, 47762306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 47862306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 47962306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 48062306a36Sopenharmony_ci main_strings[strings]); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci for (i = 0; i < NUM_PF_STATS; i++, strings++, 48362306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 48462306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 48562306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 48662306a36Sopenharmony_ci main_strings[strings]); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci for (i = 0; i < NUM_FLOW_STATS; i++, strings++, 48962306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 49062306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 49162306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 49262306a36Sopenharmony_ci main_strings[strings]); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci for (i = 0; i < NUM_PKT_STATS; i++, strings++, 49562306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 49662306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 49762306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 49862306a36Sopenharmony_ci main_strings[strings]); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci for (i = 0; i < NUM_XDP_STATS; i++, strings++, 50162306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 50262306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 50362306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 50462306a36Sopenharmony_ci main_strings[strings]); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci for (i = 0; i < NUM_PHY_STATS; i++, strings++, 50762306a36Sopenharmony_ci bitmap_iterator_inc(&it)) 50862306a36Sopenharmony_ci if (bitmap_iterator_test(&it)) 50962306a36Sopenharmony_ci strcpy(data + (index++) * ETH_GSTRING_LEN, 51062306a36Sopenharmony_ci main_strings[strings]); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (i = 0; i < priv->tx_ring_num[TX]; i++) { 51362306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 51462306a36Sopenharmony_ci "tx%d_packets", i); 51562306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 51662306a36Sopenharmony_ci "tx%d_bytes", i); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci for (i = 0; i < priv->rx_ring_num; i++) { 51962306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 52062306a36Sopenharmony_ci "rx%d_packets", i); 52162306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 52262306a36Sopenharmony_ci "rx%d_bytes", i); 52362306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 52462306a36Sopenharmony_ci "rx%d_dropped", i); 52562306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 52662306a36Sopenharmony_ci "rx%d_xdp_drop", i); 52762306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 52862306a36Sopenharmony_ci "rx%d_xdp_redirect", i); 52962306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 53062306a36Sopenharmony_ci "rx%d_xdp_redirect_fail", i); 53162306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 53262306a36Sopenharmony_ci "rx%d_xdp_tx", i); 53362306a36Sopenharmony_ci sprintf(data + (index++) * ETH_GSTRING_LEN, 53462306a36Sopenharmony_ci "rx%d_xdp_tx_full", i); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci case ETH_SS_PRIV_FLAGS: 53862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mlx4_en_priv_flags); i++) 53962306a36Sopenharmony_ci strcpy(data + i * ETH_GSTRING_LEN, 54062306a36Sopenharmony_ci mlx4_en_priv_flags[i]); 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic u32 mlx4_en_autoneg_get(struct net_device *dev) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 54962306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 55062306a36Sopenharmony_ci u32 autoneg = AUTONEG_DISABLE; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if ((mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_BACKPL_AN_REP) && 55362306a36Sopenharmony_ci (priv->port_state.flags & MLX4_EN_PORT_ANE)) 55462306a36Sopenharmony_ci autoneg = AUTONEG_ENABLE; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return autoneg; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void ptys2ethtool_update_supported_port(unsigned long *mask, 56062306a36Sopenharmony_ci struct mlx4_ptys_reg *ptys_reg) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci u32 eth_proto = be32_to_cpu(ptys_reg->eth_proto_cap); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_T) 56562306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_1000BASE_T) 56662306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_100BASE_TX))) { 56762306a36Sopenharmony_ci __set_bit(ETHTOOL_LINK_MODE_TP_BIT, mask); 56862306a36Sopenharmony_ci } else if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_CR) 56962306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_10GBASE_SR) 57062306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_56GBASE_SR4) 57162306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_40GBASE_CR4) 57262306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_40GBASE_SR4) 57362306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII))) { 57462306a36Sopenharmony_ci __set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, mask); 57562306a36Sopenharmony_ci } else if (eth_proto & (MLX4_PROT_MASK(MLX4_56GBASE_KR4) 57662306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_40GBASE_KR4) 57762306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_20GBASE_KR2) 57862306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_10GBASE_KR) 57962306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_10GBASE_KX4) 58062306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_1000BASE_KX))) { 58162306a36Sopenharmony_ci __set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, mask); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic u32 ptys_get_active_port(struct mlx4_ptys_reg *ptys_reg) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci u32 eth_proto = be32_to_cpu(ptys_reg->eth_proto_oper); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!eth_proto) /* link down */ 59062306a36Sopenharmony_ci eth_proto = be32_to_cpu(ptys_reg->eth_proto_cap); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_T) 59362306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_1000BASE_T) 59462306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_100BASE_TX))) { 59562306a36Sopenharmony_ci return PORT_TP; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_SR) 59962306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_56GBASE_SR4) 60062306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_40GBASE_SR4) 60162306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII))) { 60262306a36Sopenharmony_ci return PORT_FIBRE; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_CR) 60662306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_56GBASE_CR4) 60762306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_40GBASE_CR4))) { 60862306a36Sopenharmony_ci return PORT_DA; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (eth_proto & (MLX4_PROT_MASK(MLX4_56GBASE_KR4) 61262306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_40GBASE_KR4) 61362306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_20GBASE_KR2) 61462306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_10GBASE_KR) 61562306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_10GBASE_KX4) 61662306a36Sopenharmony_ci | MLX4_PROT_MASK(MLX4_1000BASE_KX))) { 61762306a36Sopenharmony_ci return PORT_NONE; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci return PORT_OTHER; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci#define MLX4_LINK_MODES_SZ \ 62362306a36Sopenharmony_ci (sizeof_field(struct mlx4_ptys_reg, eth_proto_cap) * 8) 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cienum ethtool_report { 62662306a36Sopenharmony_ci SUPPORTED = 0, 62762306a36Sopenharmony_ci ADVERTISED = 1, 62862306a36Sopenharmony_ci}; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistruct ptys2ethtool_config { 63162306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); 63262306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(advertised); 63362306a36Sopenharmony_ci u32 speed; 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic unsigned long *ptys2ethtool_link_mode(struct ptys2ethtool_config *cfg, 63762306a36Sopenharmony_ci enum ethtool_report report) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci switch (report) { 64062306a36Sopenharmony_ci case SUPPORTED: 64162306a36Sopenharmony_ci return cfg->supported; 64262306a36Sopenharmony_ci case ADVERTISED: 64362306a36Sopenharmony_ci return cfg->advertised; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci return NULL; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci#define MLX4_BUILD_PTYS2ETHTOOL_CONFIG(reg_, speed_, ...) \ 64962306a36Sopenharmony_ci ({ \ 65062306a36Sopenharmony_ci struct ptys2ethtool_config *cfg; \ 65162306a36Sopenharmony_ci static const unsigned int modes[] = { __VA_ARGS__ }; \ 65262306a36Sopenharmony_ci unsigned int i; \ 65362306a36Sopenharmony_ci cfg = &ptys2ethtool_map[reg_]; \ 65462306a36Sopenharmony_ci cfg->speed = speed_; \ 65562306a36Sopenharmony_ci linkmode_zero(cfg->supported); \ 65662306a36Sopenharmony_ci linkmode_zero(cfg->advertised); \ 65762306a36Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(modes) ; ++i) { \ 65862306a36Sopenharmony_ci __set_bit(modes[i], cfg->supported); \ 65962306a36Sopenharmony_ci __set_bit(modes[i], cfg->advertised); \ 66062306a36Sopenharmony_ci } \ 66162306a36Sopenharmony_ci }) 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* Translates mlx4 link mode to equivalent ethtool Link modes/speed */ 66462306a36Sopenharmony_cistatic struct ptys2ethtool_config ptys2ethtool_map[MLX4_LINK_MODES_SZ]; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_civoid __init mlx4_en_init_ptys2ethtool_map(void) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_100BASE_TX, SPEED_100, 66962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_100baseT_Full_BIT); 67062306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_T, SPEED_1000, 67162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseT_Full_BIT); 67262306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_CX_SGMII, SPEED_1000, 67362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseX_Full_BIT); 67462306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_1000BASE_KX, SPEED_1000, 67562306a36Sopenharmony_ci ETHTOOL_LINK_MODE_1000baseKX_Full_BIT); 67662306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_T, SPEED_10000, 67762306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseT_Full_BIT); 67862306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_CX4, SPEED_10000, 67962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT); 68062306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_KX4, SPEED_10000, 68162306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT); 68262306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_KR, SPEED_10000, 68362306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseKR_Full_BIT); 68462306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_CR, SPEED_10000, 68562306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseCR_Full_BIT); 68662306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_10GBASE_SR, SPEED_10000, 68762306a36Sopenharmony_ci ETHTOOL_LINK_MODE_10000baseSR_Full_BIT); 68862306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_20GBASE_KR2, SPEED_20000, 68962306a36Sopenharmony_ci ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, 69062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT); 69162306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_40GBASE_CR4, SPEED_40000, 69262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT); 69362306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_40GBASE_KR4, SPEED_40000, 69462306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT); 69562306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_40GBASE_SR4, SPEED_40000, 69662306a36Sopenharmony_ci ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT); 69762306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_56GBASE_KR4, SPEED_56000, 69862306a36Sopenharmony_ci ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT); 69962306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_56GBASE_CR4, SPEED_56000, 70062306a36Sopenharmony_ci ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT); 70162306a36Sopenharmony_ci MLX4_BUILD_PTYS2ETHTOOL_CONFIG(MLX4_56GBASE_SR4, SPEED_56000, 70262306a36Sopenharmony_ci ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT); 70362306a36Sopenharmony_ci}; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void ptys2ethtool_update_link_modes(unsigned long *link_modes, 70662306a36Sopenharmony_ci u32 eth_proto, 70762306a36Sopenharmony_ci enum ethtool_report report) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci int i; 71062306a36Sopenharmony_ci for (i = 0; i < MLX4_LINK_MODES_SZ; i++) { 71162306a36Sopenharmony_ci if (eth_proto & MLX4_PROT_MASK(i)) 71262306a36Sopenharmony_ci linkmode_or(link_modes, link_modes, 71362306a36Sopenharmony_ci ptys2ethtool_link_mode(&ptys2ethtool_map[i], report)); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic u32 ethtool2ptys_link_modes(const unsigned long *link_modes, 71862306a36Sopenharmony_ci enum ethtool_report report) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci int i; 72162306a36Sopenharmony_ci u32 ptys_modes = 0; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci for (i = 0; i < MLX4_LINK_MODES_SZ; i++) { 72462306a36Sopenharmony_ci ulong *map_mode = ptys2ethtool_link_mode(&ptys2ethtool_map[i], 72562306a36Sopenharmony_ci report); 72662306a36Sopenharmony_ci if (linkmode_intersects(map_mode, link_modes)) 72762306a36Sopenharmony_ci ptys_modes |= 1 << i; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci return ptys_modes; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/* Convert actual speed (SPEED_XXX) to ptys link modes */ 73362306a36Sopenharmony_cistatic u32 speed2ptys_link_modes(u32 speed) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci int i; 73662306a36Sopenharmony_ci u32 ptys_modes = 0; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci for (i = 0; i < MLX4_LINK_MODES_SZ; i++) { 73962306a36Sopenharmony_ci if (ptys2ethtool_map[i].speed == speed) 74062306a36Sopenharmony_ci ptys_modes |= 1 << i; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci return ptys_modes; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int 74662306a36Sopenharmony_ciethtool_get_ptys_link_ksettings(struct net_device *dev, 74762306a36Sopenharmony_ci struct ethtool_link_ksettings *link_ksettings) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 75062306a36Sopenharmony_ci struct mlx4_ptys_reg ptys_reg; 75162306a36Sopenharmony_ci u32 eth_proto; 75262306a36Sopenharmony_ci int ret; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci memset(&ptys_reg, 0, sizeof(ptys_reg)); 75562306a36Sopenharmony_ci ptys_reg.local_port = priv->port; 75662306a36Sopenharmony_ci ptys_reg.proto_mask = MLX4_PTYS_EN; 75762306a36Sopenharmony_ci ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev, 75862306a36Sopenharmony_ci MLX4_ACCESS_REG_QUERY, &ptys_reg); 75962306a36Sopenharmony_ci if (ret) { 76062306a36Sopenharmony_ci en_warn(priv, "Failed to run mlx4_ACCESS_PTYS_REG status(%x)", 76162306a36Sopenharmony_ci ret); 76262306a36Sopenharmony_ci return ret; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci en_dbg(DRV, priv, "ptys_reg.proto_mask %x\n", 76562306a36Sopenharmony_ci ptys_reg.proto_mask); 76662306a36Sopenharmony_ci en_dbg(DRV, priv, "ptys_reg.eth_proto_cap %x\n", 76762306a36Sopenharmony_ci be32_to_cpu(ptys_reg.eth_proto_cap)); 76862306a36Sopenharmony_ci en_dbg(DRV, priv, "ptys_reg.eth_proto_admin %x\n", 76962306a36Sopenharmony_ci be32_to_cpu(ptys_reg.eth_proto_admin)); 77062306a36Sopenharmony_ci en_dbg(DRV, priv, "ptys_reg.eth_proto_oper %x\n", 77162306a36Sopenharmony_ci be32_to_cpu(ptys_reg.eth_proto_oper)); 77262306a36Sopenharmony_ci en_dbg(DRV, priv, "ptys_reg.eth_proto_lp_adv %x\n", 77362306a36Sopenharmony_ci be32_to_cpu(ptys_reg.eth_proto_lp_adv)); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* reset supported/advertising masks */ 77662306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); 77762306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ptys2ethtool_update_supported_port(link_ksettings->link_modes.supported, 78062306a36Sopenharmony_ci &ptys_reg); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci eth_proto = be32_to_cpu(ptys_reg.eth_proto_cap); 78362306a36Sopenharmony_ci ptys2ethtool_update_link_modes(link_ksettings->link_modes.supported, 78462306a36Sopenharmony_ci eth_proto, SUPPORTED); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci eth_proto = be32_to_cpu(ptys_reg.eth_proto_admin); 78762306a36Sopenharmony_ci ptys2ethtool_update_link_modes(link_ksettings->link_modes.advertising, 78862306a36Sopenharmony_ci eth_proto, ADVERTISED); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, supported, 79162306a36Sopenharmony_ci Pause); 79262306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, supported, 79362306a36Sopenharmony_ci Asym_Pause); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (priv->prof->tx_pause) 79662306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 79762306a36Sopenharmony_ci advertising, Pause); 79862306a36Sopenharmony_ci if (priv->prof->tx_pause ^ priv->prof->rx_pause) 79962306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 80062306a36Sopenharmony_ci advertising, Asym_Pause); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci link_ksettings->base.port = ptys_get_active_port(&ptys_reg); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (mlx4_en_autoneg_get(dev)) { 80562306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 80662306a36Sopenharmony_ci supported, Autoneg); 80762306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 80862306a36Sopenharmony_ci advertising, Autoneg); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci link_ksettings->base.autoneg 81262306a36Sopenharmony_ci = (priv->port_state.flags & MLX4_EN_PORT_ANC) ? 81362306a36Sopenharmony_ci AUTONEG_ENABLE : AUTONEG_DISABLE; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci eth_proto = be32_to_cpu(ptys_reg.eth_proto_lp_adv); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); 81862306a36Sopenharmony_ci ptys2ethtool_update_link_modes( 81962306a36Sopenharmony_ci link_ksettings->link_modes.lp_advertising, 82062306a36Sopenharmony_ci eth_proto, ADVERTISED); 82162306a36Sopenharmony_ci if (priv->port_state.flags & MLX4_EN_PORT_ANC) 82262306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 82362306a36Sopenharmony_ci lp_advertising, Autoneg); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci link_ksettings->base.phy_address = 0; 82662306a36Sopenharmony_ci link_ksettings->base.mdio_support = 0; 82762306a36Sopenharmony_ci link_ksettings->base.eth_tp_mdix = ETH_TP_MDI_INVALID; 82862306a36Sopenharmony_ci link_ksettings->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return ret; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic void 83462306a36Sopenharmony_ciethtool_get_default_link_ksettings( 83562306a36Sopenharmony_ci struct net_device *dev, struct ethtool_link_ksettings *link_ksettings) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 83862306a36Sopenharmony_ci int trans_type; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci link_ksettings->base.autoneg = AUTONEG_DISABLE; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); 84362306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, supported, 84462306a36Sopenharmony_ci 10000baseT_Full); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); 84762306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, advertising, 84862306a36Sopenharmony_ci 10000baseT_Full); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci trans_type = priv->port_state.transceiver; 85162306a36Sopenharmony_ci if (trans_type > 0 && trans_type <= 0xC) { 85262306a36Sopenharmony_ci link_ksettings->base.port = PORT_FIBRE; 85362306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 85462306a36Sopenharmony_ci supported, FIBRE); 85562306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 85662306a36Sopenharmony_ci advertising, FIBRE); 85762306a36Sopenharmony_ci } else if (trans_type == 0x80 || trans_type == 0) { 85862306a36Sopenharmony_ci link_ksettings->base.port = PORT_TP; 85962306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 86062306a36Sopenharmony_ci supported, TP); 86162306a36Sopenharmony_ci ethtool_link_ksettings_add_link_mode(link_ksettings, 86262306a36Sopenharmony_ci advertising, TP); 86362306a36Sopenharmony_ci } else { 86462306a36Sopenharmony_ci link_ksettings->base.port = -1; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic int 86962306a36Sopenharmony_cimlx4_en_get_link_ksettings(struct net_device *dev, 87062306a36Sopenharmony_ci struct ethtool_link_ksettings *link_ksettings) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 87362306a36Sopenharmony_ci int ret = -EINVAL; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (mlx4_en_QUERY_PORT(priv->mdev, priv->port)) 87662306a36Sopenharmony_ci return -ENOMEM; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci en_dbg(DRV, priv, "query port state.flags ANC(%x) ANE(%x)\n", 87962306a36Sopenharmony_ci priv->port_state.flags & MLX4_EN_PORT_ANC, 88062306a36Sopenharmony_ci priv->port_state.flags & MLX4_EN_PORT_ANE); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL) 88362306a36Sopenharmony_ci ret = ethtool_get_ptys_link_ksettings(dev, link_ksettings); 88462306a36Sopenharmony_ci if (ret) /* ETH PROT CRTL is not supported or PTYS CMD failed */ 88562306a36Sopenharmony_ci ethtool_get_default_link_ksettings(dev, link_ksettings); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 88862306a36Sopenharmony_ci link_ksettings->base.speed = priv->port_state.link_speed; 88962306a36Sopenharmony_ci link_ksettings->base.duplex = DUPLEX_FULL; 89062306a36Sopenharmony_ci } else { 89162306a36Sopenharmony_ci link_ksettings->base.speed = SPEED_UNKNOWN; 89262306a36Sopenharmony_ci link_ksettings->base.duplex = DUPLEX_UNKNOWN; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci/* Calculate PTYS admin according ethtool speed (SPEED_XXX) */ 89862306a36Sopenharmony_cistatic __be32 speed_set_ptys_admin(struct mlx4_en_priv *priv, u32 speed, 89962306a36Sopenharmony_ci __be32 proto_cap) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci __be32 proto_admin = 0; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (!speed) { /* Speed = 0 ==> Reset Link modes */ 90462306a36Sopenharmony_ci proto_admin = proto_cap; 90562306a36Sopenharmony_ci en_info(priv, "Speed was set to 0, Reset advertised Link Modes to default (%x)\n", 90662306a36Sopenharmony_ci be32_to_cpu(proto_cap)); 90762306a36Sopenharmony_ci } else { 90862306a36Sopenharmony_ci u32 ptys_link_modes = speed2ptys_link_modes(speed); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci proto_admin = cpu_to_be32(ptys_link_modes) & proto_cap; 91162306a36Sopenharmony_ci en_info(priv, "Setting Speed to %d\n", speed); 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci return proto_admin; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int 91762306a36Sopenharmony_cimlx4_en_set_link_ksettings(struct net_device *dev, 91862306a36Sopenharmony_ci const struct ethtool_link_ksettings *link_ksettings) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 92162306a36Sopenharmony_ci struct mlx4_ptys_reg ptys_reg; 92262306a36Sopenharmony_ci __be32 proto_admin; 92362306a36Sopenharmony_ci u8 cur_autoneg; 92462306a36Sopenharmony_ci int ret; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci u32 ptys_adv = ethtool2ptys_link_modes( 92762306a36Sopenharmony_ci link_ksettings->link_modes.advertising, ADVERTISED); 92862306a36Sopenharmony_ci const int speed = link_ksettings->base.speed; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci en_dbg(DRV, priv, 93162306a36Sopenharmony_ci "Set Speed=%d adv={%*pbl} autoneg=%d duplex=%d\n", 93262306a36Sopenharmony_ci speed, __ETHTOOL_LINK_MODE_MASK_NBITS, 93362306a36Sopenharmony_ci link_ksettings->link_modes.advertising, 93462306a36Sopenharmony_ci link_ksettings->base.autoneg, 93562306a36Sopenharmony_ci link_ksettings->base.duplex); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (!(priv->mdev->dev->caps.flags2 & 93862306a36Sopenharmony_ci MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL) || 93962306a36Sopenharmony_ci (link_ksettings->base.duplex == DUPLEX_HALF)) 94062306a36Sopenharmony_ci return -EINVAL; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci memset(&ptys_reg, 0, sizeof(ptys_reg)); 94362306a36Sopenharmony_ci ptys_reg.local_port = priv->port; 94462306a36Sopenharmony_ci ptys_reg.proto_mask = MLX4_PTYS_EN; 94562306a36Sopenharmony_ci ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev, 94662306a36Sopenharmony_ci MLX4_ACCESS_REG_QUERY, &ptys_reg); 94762306a36Sopenharmony_ci if (ret) { 94862306a36Sopenharmony_ci en_warn(priv, "Failed to QUERY mlx4_ACCESS_PTYS_REG status(%x)\n", 94962306a36Sopenharmony_ci ret); 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci cur_autoneg = ptys_reg.flags & MLX4_PTYS_AN_DISABLE_ADMIN ? 95462306a36Sopenharmony_ci AUTONEG_DISABLE : AUTONEG_ENABLE; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (link_ksettings->base.autoneg == AUTONEG_DISABLE) { 95762306a36Sopenharmony_ci proto_admin = speed_set_ptys_admin(priv, speed, 95862306a36Sopenharmony_ci ptys_reg.eth_proto_cap); 95962306a36Sopenharmony_ci if ((be32_to_cpu(proto_admin) & 96062306a36Sopenharmony_ci (MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII) | 96162306a36Sopenharmony_ci MLX4_PROT_MASK(MLX4_1000BASE_KX))) && 96262306a36Sopenharmony_ci (ptys_reg.flags & MLX4_PTYS_AN_DISABLE_CAP)) 96362306a36Sopenharmony_ci ptys_reg.flags |= MLX4_PTYS_AN_DISABLE_ADMIN; 96462306a36Sopenharmony_ci } else { 96562306a36Sopenharmony_ci proto_admin = cpu_to_be32(ptys_adv); 96662306a36Sopenharmony_ci ptys_reg.flags &= ~MLX4_PTYS_AN_DISABLE_ADMIN; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci proto_admin &= ptys_reg.eth_proto_cap; 97062306a36Sopenharmony_ci if (!proto_admin) { 97162306a36Sopenharmony_ci en_warn(priv, "Not supported link mode(s) requested, check supported link modes.\n"); 97262306a36Sopenharmony_ci return -EINVAL; /* nothing to change due to bad input */ 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if ((proto_admin == ptys_reg.eth_proto_admin) && 97662306a36Sopenharmony_ci ((ptys_reg.flags & MLX4_PTYS_AN_DISABLE_CAP) && 97762306a36Sopenharmony_ci (link_ksettings->base.autoneg == cur_autoneg))) 97862306a36Sopenharmony_ci return 0; /* Nothing to change */ 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci en_dbg(DRV, priv, "mlx4_ACCESS_PTYS_REG SET: ptys_reg.eth_proto_admin = 0x%x\n", 98162306a36Sopenharmony_ci be32_to_cpu(proto_admin)); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci ptys_reg.eth_proto_admin = proto_admin; 98462306a36Sopenharmony_ci ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev, MLX4_ACCESS_REG_WRITE, 98562306a36Sopenharmony_ci &ptys_reg); 98662306a36Sopenharmony_ci if (ret) { 98762306a36Sopenharmony_ci en_warn(priv, "Failed to write mlx4_ACCESS_PTYS_REG eth_proto_admin(0x%x) status(0x%x)", 98862306a36Sopenharmony_ci be32_to_cpu(ptys_reg.eth_proto_admin), ret); 98962306a36Sopenharmony_ci return ret; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci mutex_lock(&priv->mdev->state_lock); 99362306a36Sopenharmony_ci if (priv->port_up) { 99462306a36Sopenharmony_ci en_warn(priv, "Port link mode changed, restarting port...\n"); 99562306a36Sopenharmony_ci mlx4_en_stop_port(dev, 1); 99662306a36Sopenharmony_ci if (mlx4_en_start_port(dev)) 99762306a36Sopenharmony_ci en_err(priv, "Failed restarting port %d\n", priv->port); 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci mutex_unlock(&priv->mdev->state_lock); 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int mlx4_en_get_coalesce(struct net_device *dev, 100462306a36Sopenharmony_ci struct ethtool_coalesce *coal, 100562306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 100662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci coal->tx_coalesce_usecs = priv->tx_usecs; 101162306a36Sopenharmony_ci coal->tx_max_coalesced_frames = priv->tx_frames; 101262306a36Sopenharmony_ci coal->tx_max_coalesced_frames_irq = priv->tx_work_limit; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci coal->rx_coalesce_usecs = priv->rx_usecs; 101562306a36Sopenharmony_ci coal->rx_max_coalesced_frames = priv->rx_frames; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci coal->pkt_rate_low = priv->pkt_rate_low; 101862306a36Sopenharmony_ci coal->rx_coalesce_usecs_low = priv->rx_usecs_low; 101962306a36Sopenharmony_ci coal->pkt_rate_high = priv->pkt_rate_high; 102062306a36Sopenharmony_ci coal->rx_coalesce_usecs_high = priv->rx_usecs_high; 102162306a36Sopenharmony_ci coal->rate_sample_interval = priv->sample_interval; 102262306a36Sopenharmony_ci coal->use_adaptive_rx_coalesce = priv->adaptive_rx_coal; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int mlx4_en_set_coalesce(struct net_device *dev, 102862306a36Sopenharmony_ci struct ethtool_coalesce *coal, 102962306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 103062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (!coal->tx_max_coalesced_frames_irq) 103562306a36Sopenharmony_ci return -EINVAL; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (coal->tx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME || 103862306a36Sopenharmony_ci coal->rx_coalesce_usecs > MLX4_EN_MAX_COAL_TIME || 103962306a36Sopenharmony_ci coal->rx_coalesce_usecs_low > MLX4_EN_MAX_COAL_TIME || 104062306a36Sopenharmony_ci coal->rx_coalesce_usecs_high > MLX4_EN_MAX_COAL_TIME) { 104162306a36Sopenharmony_ci netdev_info(dev, "%s: maximum coalesce time supported is %d usecs\n", 104262306a36Sopenharmony_ci __func__, MLX4_EN_MAX_COAL_TIME); 104362306a36Sopenharmony_ci return -ERANGE; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (coal->tx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS || 104762306a36Sopenharmony_ci coal->rx_max_coalesced_frames > MLX4_EN_MAX_COAL_PKTS) { 104862306a36Sopenharmony_ci netdev_info(dev, "%s: maximum coalesced frames supported is %d\n", 104962306a36Sopenharmony_ci __func__, MLX4_EN_MAX_COAL_PKTS); 105062306a36Sopenharmony_ci return -ERANGE; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci priv->rx_frames = (coal->rx_max_coalesced_frames == 105462306a36Sopenharmony_ci MLX4_EN_AUTO_CONF) ? 105562306a36Sopenharmony_ci MLX4_EN_RX_COAL_TARGET : 105662306a36Sopenharmony_ci coal->rx_max_coalesced_frames; 105762306a36Sopenharmony_ci priv->rx_usecs = (coal->rx_coalesce_usecs == 105862306a36Sopenharmony_ci MLX4_EN_AUTO_CONF) ? 105962306a36Sopenharmony_ci MLX4_EN_RX_COAL_TIME : 106062306a36Sopenharmony_ci coal->rx_coalesce_usecs; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* Setting TX coalescing parameters */ 106362306a36Sopenharmony_ci if (coal->tx_coalesce_usecs != priv->tx_usecs || 106462306a36Sopenharmony_ci coal->tx_max_coalesced_frames != priv->tx_frames) { 106562306a36Sopenharmony_ci priv->tx_usecs = coal->tx_coalesce_usecs; 106662306a36Sopenharmony_ci priv->tx_frames = coal->tx_max_coalesced_frames; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* Set adaptive coalescing params */ 107062306a36Sopenharmony_ci priv->pkt_rate_low = coal->pkt_rate_low; 107162306a36Sopenharmony_ci priv->rx_usecs_low = coal->rx_coalesce_usecs_low; 107262306a36Sopenharmony_ci priv->pkt_rate_high = coal->pkt_rate_high; 107362306a36Sopenharmony_ci priv->rx_usecs_high = coal->rx_coalesce_usecs_high; 107462306a36Sopenharmony_ci priv->sample_interval = coal->rate_sample_interval; 107562306a36Sopenharmony_ci priv->adaptive_rx_coal = coal->use_adaptive_rx_coalesce; 107662306a36Sopenharmony_ci priv->tx_work_limit = coal->tx_max_coalesced_frames_irq; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return mlx4_en_moderation_update(priv); 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic int mlx4_en_set_pauseparam(struct net_device *dev, 108262306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 108562306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 108662306a36Sopenharmony_ci u8 tx_pause, tx_ppp, rx_pause, rx_ppp; 108762306a36Sopenharmony_ci int err; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (pause->autoneg) 109062306a36Sopenharmony_ci return -EINVAL; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci tx_pause = !!(pause->tx_pause); 109362306a36Sopenharmony_ci rx_pause = !!(pause->rx_pause); 109462306a36Sopenharmony_ci rx_ppp = (tx_pause || rx_pause) ? 0 : priv->prof->rx_ppp; 109562306a36Sopenharmony_ci tx_ppp = (tx_pause || rx_pause) ? 0 : priv->prof->tx_ppp; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci err = mlx4_SET_PORT_general(mdev->dev, priv->port, 109862306a36Sopenharmony_ci priv->rx_skb_size + ETH_FCS_LEN, 109962306a36Sopenharmony_ci tx_pause, tx_ppp, rx_pause, rx_ppp); 110062306a36Sopenharmony_ci if (err) { 110162306a36Sopenharmony_ci en_err(priv, "Failed setting pause params, err = %d\n", err); 110262306a36Sopenharmony_ci return err; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci mlx4_en_update_pfc_stats_bitmap(mdev->dev, &priv->stats_bitmap, 110662306a36Sopenharmony_ci rx_ppp, rx_pause, tx_ppp, tx_pause); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci priv->prof->tx_pause = tx_pause; 110962306a36Sopenharmony_ci priv->prof->rx_pause = rx_pause; 111062306a36Sopenharmony_ci priv->prof->tx_ppp = tx_ppp; 111162306a36Sopenharmony_ci priv->prof->rx_ppp = rx_ppp; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci return err; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic void mlx4_en_get_pause_stats(struct net_device *dev, 111762306a36Sopenharmony_ci struct ethtool_pause_stats *stats) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 112062306a36Sopenharmony_ci struct bitmap_iterator it; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci bitmap_iterator_init(&it, priv->stats_bitmap.bitmap, NUM_ALL_STATS); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci spin_lock_bh(&priv->stats_lock); 112562306a36Sopenharmony_ci if (test_bit(FLOW_PRIORITY_STATS_IDX_TX_FRAMES, 112662306a36Sopenharmony_ci priv->stats_bitmap.bitmap)) 112762306a36Sopenharmony_ci stats->tx_pause_frames = priv->tx_flowstats.tx_pause; 112862306a36Sopenharmony_ci if (test_bit(FLOW_PRIORITY_STATS_IDX_RX_FRAMES, 112962306a36Sopenharmony_ci priv->stats_bitmap.bitmap)) 113062306a36Sopenharmony_ci stats->rx_pause_frames = priv->rx_flowstats.rx_pause; 113162306a36Sopenharmony_ci spin_unlock_bh(&priv->stats_lock); 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic void mlx4_en_get_pauseparam(struct net_device *dev, 113562306a36Sopenharmony_ci struct ethtool_pauseparam *pause) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci pause->tx_pause = priv->prof->tx_pause; 114062306a36Sopenharmony_ci pause->rx_pause = priv->prof->rx_pause; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic int mlx4_en_set_ringparam(struct net_device *dev, 114462306a36Sopenharmony_ci struct ethtool_ringparam *param, 114562306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_param, 114662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 114962306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 115062306a36Sopenharmony_ci struct mlx4_en_port_profile new_prof; 115162306a36Sopenharmony_ci struct mlx4_en_priv *tmp; 115262306a36Sopenharmony_ci u32 rx_size, tx_size; 115362306a36Sopenharmony_ci int port_up = 0; 115462306a36Sopenharmony_ci int err = 0; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (param->rx_jumbo_pending || param->rx_mini_pending) 115762306a36Sopenharmony_ci return -EINVAL; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (param->rx_pending < MLX4_EN_MIN_RX_SIZE) { 116062306a36Sopenharmony_ci en_warn(priv, "%s: rx_pending (%d) < min (%d)\n", 116162306a36Sopenharmony_ci __func__, param->rx_pending, 116262306a36Sopenharmony_ci MLX4_EN_MIN_RX_SIZE); 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci if (param->tx_pending < MLX4_EN_MIN_TX_SIZE) { 116662306a36Sopenharmony_ci en_warn(priv, "%s: tx_pending (%d) < min (%lu)\n", 116762306a36Sopenharmony_ci __func__, param->tx_pending, 116862306a36Sopenharmony_ci MLX4_EN_MIN_TX_SIZE); 116962306a36Sopenharmony_ci return -EINVAL; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci rx_size = roundup_pow_of_two(param->rx_pending); 117362306a36Sopenharmony_ci tx_size = roundup_pow_of_two(param->tx_pending); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size : 117662306a36Sopenharmony_ci priv->rx_ring[0]->size) && 117762306a36Sopenharmony_ci tx_size == priv->tx_ring[TX][0]->size) 117862306a36Sopenharmony_ci return 0; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); 118162306a36Sopenharmony_ci if (!tmp) 118262306a36Sopenharmony_ci return -ENOMEM; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci mutex_lock(&mdev->state_lock); 118562306a36Sopenharmony_ci memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile)); 118662306a36Sopenharmony_ci new_prof.tx_ring_size = tx_size; 118762306a36Sopenharmony_ci new_prof.rx_ring_size = rx_size; 118862306a36Sopenharmony_ci err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, true); 118962306a36Sopenharmony_ci if (err) 119062306a36Sopenharmony_ci goto out; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (priv->port_up) { 119362306a36Sopenharmony_ci port_up = 1; 119462306a36Sopenharmony_ci mlx4_en_stop_port(dev, 1); 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci mlx4_en_safe_replace_resources(priv, tmp); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (port_up) { 120062306a36Sopenharmony_ci err = mlx4_en_start_port(dev); 120162306a36Sopenharmony_ci if (err) 120262306a36Sopenharmony_ci en_err(priv, "Failed starting port\n"); 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci err = mlx4_en_moderation_update(priv); 120662306a36Sopenharmony_ciout: 120762306a36Sopenharmony_ci kfree(tmp); 120862306a36Sopenharmony_ci mutex_unlock(&mdev->state_lock); 120962306a36Sopenharmony_ci return err; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic void mlx4_en_get_ringparam(struct net_device *dev, 121362306a36Sopenharmony_ci struct ethtool_ringparam *param, 121462306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_param, 121562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci memset(param, 0, sizeof(*param)); 122062306a36Sopenharmony_ci param->rx_max_pending = MLX4_EN_MAX_RX_SIZE; 122162306a36Sopenharmony_ci param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; 122262306a36Sopenharmony_ci param->rx_pending = priv->port_up ? 122362306a36Sopenharmony_ci priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size; 122462306a36Sopenharmony_ci param->tx_pending = priv->tx_ring[TX][0]->size; 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci return rounddown_pow_of_two(priv->rx_ring_num); 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic u32 mlx4_en_get_rxfh_key_size(struct net_device *netdev) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci return MLX4_EN_RSS_KEY_SIZE; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic int mlx4_en_check_rxfh_func(struct net_device *dev, u8 hfunc) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* check if requested function is supported by the device */ 124462306a36Sopenharmony_ci if (hfunc == ETH_RSS_HASH_TOP) { 124562306a36Sopenharmony_ci if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_TOP)) 124662306a36Sopenharmony_ci return -EINVAL; 124762306a36Sopenharmony_ci if (!(dev->features & NETIF_F_RXHASH)) 124862306a36Sopenharmony_ci en_warn(priv, "Toeplitz hash function should be used in conjunction with RX hashing for optimal performance\n"); 124962306a36Sopenharmony_ci return 0; 125062306a36Sopenharmony_ci } else if (hfunc == ETH_RSS_HASH_XOR) { 125162306a36Sopenharmony_ci if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RSS_XOR)) 125262306a36Sopenharmony_ci return -EINVAL; 125362306a36Sopenharmony_ci if (dev->features & NETIF_F_RXHASH) 125462306a36Sopenharmony_ci en_warn(priv, "Enabling both XOR Hash function and RX Hashing can limit RPS functionality\n"); 125562306a36Sopenharmony_ci return 0; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci return -EINVAL; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key, 126262306a36Sopenharmony_ci u8 *hfunc) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 126562306a36Sopenharmony_ci u32 n = mlx4_en_get_rxfh_indir_size(dev); 126662306a36Sopenharmony_ci u32 i, rss_rings; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci rss_rings = priv->prof->rss_rings ?: n; 126962306a36Sopenharmony_ci rss_rings = rounddown_pow_of_two(rss_rings); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci for (i = 0; i < n; i++) { 127262306a36Sopenharmony_ci if (!ring_index) 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci ring_index[i] = i % rss_rings; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci if (key) 127762306a36Sopenharmony_ci memcpy(key, priv->rss_key, MLX4_EN_RSS_KEY_SIZE); 127862306a36Sopenharmony_ci if (hfunc) 127962306a36Sopenharmony_ci *hfunc = priv->rss_hash_fn; 128062306a36Sopenharmony_ci return 0; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic int mlx4_en_set_rxfh(struct net_device *dev, const u32 *ring_index, 128462306a36Sopenharmony_ci const u8 *key, const u8 hfunc) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 128762306a36Sopenharmony_ci u32 n = mlx4_en_get_rxfh_indir_size(dev); 128862306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 128962306a36Sopenharmony_ci int port_up = 0; 129062306a36Sopenharmony_ci int err = 0; 129162306a36Sopenharmony_ci int i; 129262306a36Sopenharmony_ci int rss_rings = 0; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* Calculate RSS table size and make sure flows are spread evenly 129562306a36Sopenharmony_ci * between rings 129662306a36Sopenharmony_ci */ 129762306a36Sopenharmony_ci for (i = 0; i < n; i++) { 129862306a36Sopenharmony_ci if (!ring_index) 129962306a36Sopenharmony_ci break; 130062306a36Sopenharmony_ci if (i > 0 && !ring_index[i] && !rss_rings) 130162306a36Sopenharmony_ci rss_rings = i; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (ring_index[i] != (i % (rss_rings ?: n))) 130462306a36Sopenharmony_ci return -EINVAL; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (!rss_rings) 130862306a36Sopenharmony_ci rss_rings = n; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* RSS table size must be an order of 2 */ 131162306a36Sopenharmony_ci if (!is_power_of_2(rss_rings)) 131262306a36Sopenharmony_ci return -EINVAL; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE) { 131562306a36Sopenharmony_ci err = mlx4_en_check_rxfh_func(dev, hfunc); 131662306a36Sopenharmony_ci if (err) 131762306a36Sopenharmony_ci return err; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci mutex_lock(&mdev->state_lock); 132162306a36Sopenharmony_ci if (priv->port_up) { 132262306a36Sopenharmony_ci port_up = 1; 132362306a36Sopenharmony_ci mlx4_en_stop_port(dev, 1); 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (ring_index) 132762306a36Sopenharmony_ci priv->prof->rss_rings = rss_rings; 132862306a36Sopenharmony_ci if (key) 132962306a36Sopenharmony_ci memcpy(priv->rss_key, key, MLX4_EN_RSS_KEY_SIZE); 133062306a36Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE) 133162306a36Sopenharmony_ci priv->rss_hash_fn = hfunc; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (port_up) { 133462306a36Sopenharmony_ci err = mlx4_en_start_port(dev); 133562306a36Sopenharmony_ci if (err) 133662306a36Sopenharmony_ci en_err(priv, "Failed starting port\n"); 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci mutex_unlock(&mdev->state_lock); 134062306a36Sopenharmony_ci return err; 134162306a36Sopenharmony_ci} 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci#define all_zeros_or_all_ones(field) \ 134462306a36Sopenharmony_ci ((field) == 0 || (field) == (__force typeof(field))-1) 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int mlx4_en_validate_flow(struct net_device *dev, 134762306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct ethtool_usrip4_spec *l3_mask; 135062306a36Sopenharmony_ci struct ethtool_tcpip4_spec *l4_mask; 135162306a36Sopenharmony_ci struct ethhdr *eth_mask; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) 135462306a36Sopenharmony_ci return -EINVAL; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (cmd->fs.flow_type & FLOW_MAC_EXT) { 135762306a36Sopenharmony_ci /* dest mac mask must be ff:ff:ff:ff:ff:ff */ 135862306a36Sopenharmony_ci if (!is_broadcast_ether_addr(cmd->fs.m_ext.h_dest)) 135962306a36Sopenharmony_ci return -EINVAL; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 136362306a36Sopenharmony_ci case TCP_V4_FLOW: 136462306a36Sopenharmony_ci case UDP_V4_FLOW: 136562306a36Sopenharmony_ci if (cmd->fs.m_u.tcp_ip4_spec.tos) 136662306a36Sopenharmony_ci return -EINVAL; 136762306a36Sopenharmony_ci l4_mask = &cmd->fs.m_u.tcp_ip4_spec; 136862306a36Sopenharmony_ci /* don't allow mask which isn't all 0 or 1 */ 136962306a36Sopenharmony_ci if (!all_zeros_or_all_ones(l4_mask->ip4src) || 137062306a36Sopenharmony_ci !all_zeros_or_all_ones(l4_mask->ip4dst) || 137162306a36Sopenharmony_ci !all_zeros_or_all_ones(l4_mask->psrc) || 137262306a36Sopenharmony_ci !all_zeros_or_all_ones(l4_mask->pdst)) 137362306a36Sopenharmony_ci return -EINVAL; 137462306a36Sopenharmony_ci break; 137562306a36Sopenharmony_ci case IP_USER_FLOW: 137662306a36Sopenharmony_ci l3_mask = &cmd->fs.m_u.usr_ip4_spec; 137762306a36Sopenharmony_ci if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto || 137862306a36Sopenharmony_ci cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 || 137962306a36Sopenharmony_ci (!l3_mask->ip4src && !l3_mask->ip4dst) || 138062306a36Sopenharmony_ci !all_zeros_or_all_ones(l3_mask->ip4src) || 138162306a36Sopenharmony_ci !all_zeros_or_all_ones(l3_mask->ip4dst)) 138262306a36Sopenharmony_ci return -EINVAL; 138362306a36Sopenharmony_ci break; 138462306a36Sopenharmony_ci case ETHER_FLOW: 138562306a36Sopenharmony_ci eth_mask = &cmd->fs.m_u.ether_spec; 138662306a36Sopenharmony_ci /* source mac mask must not be set */ 138762306a36Sopenharmony_ci if (!is_zero_ether_addr(eth_mask->h_source)) 138862306a36Sopenharmony_ci return -EINVAL; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci /* dest mac mask must be ff:ff:ff:ff:ff:ff */ 139162306a36Sopenharmony_ci if (!is_broadcast_ether_addr(eth_mask->h_dest)) 139262306a36Sopenharmony_ci return -EINVAL; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci if (!all_zeros_or_all_ones(eth_mask->h_proto)) 139562306a36Sopenharmony_ci return -EINVAL; 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci default: 139862306a36Sopenharmony_ci return -EINVAL; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if ((cmd->fs.flow_type & FLOW_EXT)) { 140262306a36Sopenharmony_ci if (cmd->fs.m_ext.vlan_etype || 140362306a36Sopenharmony_ci !((cmd->fs.m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK)) == 140462306a36Sopenharmony_ci 0 || 140562306a36Sopenharmony_ci (cmd->fs.m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK)) == 140662306a36Sopenharmony_ci cpu_to_be16(VLAN_VID_MASK))) 140762306a36Sopenharmony_ci return -EINVAL; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (cmd->fs.m_ext.vlan_tci) { 141062306a36Sopenharmony_ci if (be16_to_cpu(cmd->fs.h_ext.vlan_tci) >= VLAN_N_VID) 141162306a36Sopenharmony_ci return -EINVAL; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci } 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci return 0; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_cistatic int mlx4_en_ethtool_add_mac_rule(struct ethtool_rxnfc *cmd, 142062306a36Sopenharmony_ci struct list_head *rule_list_h, 142162306a36Sopenharmony_ci struct mlx4_spec_list *spec_l2, 142262306a36Sopenharmony_ci unsigned char *mac) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH; 142762306a36Sopenharmony_ci memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN); 142862306a36Sopenharmony_ci memcpy(spec_l2->eth.dst_mac, mac, ETH_ALEN); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if ((cmd->fs.flow_type & FLOW_EXT) && 143162306a36Sopenharmony_ci (cmd->fs.m_ext.vlan_tci & cpu_to_be16(VLAN_VID_MASK))) { 143262306a36Sopenharmony_ci spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci; 143362306a36Sopenharmony_ci spec_l2->eth.vlan_id_msk = cpu_to_be16(VLAN_VID_MASK); 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci list_add_tail(&spec_l2->list, rule_list_h); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci return 0; 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic int mlx4_en_ethtool_add_mac_rule_by_ipv4(struct mlx4_en_priv *priv, 144262306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, 144362306a36Sopenharmony_ci struct list_head *rule_list_h, 144462306a36Sopenharmony_ci struct mlx4_spec_list *spec_l2, 144562306a36Sopenharmony_ci __be32 ipv4_dst) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci#ifdef CONFIG_INET 144862306a36Sopenharmony_ci unsigned char mac[ETH_ALEN]; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (!ipv4_is_multicast(ipv4_dst)) { 145162306a36Sopenharmony_ci if (cmd->fs.flow_type & FLOW_MAC_EXT) 145262306a36Sopenharmony_ci memcpy(&mac, cmd->fs.h_ext.h_dest, ETH_ALEN); 145362306a36Sopenharmony_ci else 145462306a36Sopenharmony_ci memcpy(&mac, priv->dev->dev_addr, ETH_ALEN); 145562306a36Sopenharmony_ci } else { 145662306a36Sopenharmony_ci ip_eth_mc_map(ipv4_dst, mac); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci return mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, &mac[0]); 146062306a36Sopenharmony_ci#else 146162306a36Sopenharmony_ci return -EINVAL; 146262306a36Sopenharmony_ci#endif 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic int add_ip_rule(struct mlx4_en_priv *priv, 146662306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, 146762306a36Sopenharmony_ci struct list_head *list_h) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci int err; 147062306a36Sopenharmony_ci struct mlx4_spec_list *spec_l2; 147162306a36Sopenharmony_ci struct mlx4_spec_list *spec_l3; 147262306a36Sopenharmony_ci struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL); 147562306a36Sopenharmony_ci spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL); 147662306a36Sopenharmony_ci if (!spec_l2 || !spec_l3) { 147762306a36Sopenharmony_ci err = -ENOMEM; 147862306a36Sopenharmony_ci goto free_spec; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci err = mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, spec_l2, 148262306a36Sopenharmony_ci cmd->fs.h_u. 148362306a36Sopenharmony_ci usr_ip4_spec.ip4dst); 148462306a36Sopenharmony_ci if (err) 148562306a36Sopenharmony_ci goto free_spec; 148662306a36Sopenharmony_ci spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; 148762306a36Sopenharmony_ci spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src; 148862306a36Sopenharmony_ci if (l3_mask->ip4src) 148962306a36Sopenharmony_ci spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK; 149062306a36Sopenharmony_ci spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst; 149162306a36Sopenharmony_ci if (l3_mask->ip4dst) 149262306a36Sopenharmony_ci spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK; 149362306a36Sopenharmony_ci list_add_tail(&spec_l3->list, list_h); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci return 0; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cifree_spec: 149862306a36Sopenharmony_ci kfree(spec_l2); 149962306a36Sopenharmony_ci kfree(spec_l3); 150062306a36Sopenharmony_ci return err; 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic int add_tcp_udp_rule(struct mlx4_en_priv *priv, 150462306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, 150562306a36Sopenharmony_ci struct list_head *list_h, int proto) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci int err; 150862306a36Sopenharmony_ci struct mlx4_spec_list *spec_l2; 150962306a36Sopenharmony_ci struct mlx4_spec_list *spec_l3; 151062306a36Sopenharmony_ci struct mlx4_spec_list *spec_l4; 151162306a36Sopenharmony_ci struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL); 151462306a36Sopenharmony_ci spec_l3 = kzalloc(sizeof(*spec_l3), GFP_KERNEL); 151562306a36Sopenharmony_ci spec_l4 = kzalloc(sizeof(*spec_l4), GFP_KERNEL); 151662306a36Sopenharmony_ci if (!spec_l2 || !spec_l3 || !spec_l4) { 151762306a36Sopenharmony_ci err = -ENOMEM; 151862306a36Sopenharmony_ci goto free_spec; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (proto == TCP_V4_FLOW) { 152462306a36Sopenharmony_ci err = mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, 152562306a36Sopenharmony_ci spec_l2, 152662306a36Sopenharmony_ci cmd->fs.h_u. 152762306a36Sopenharmony_ci tcp_ip4_spec.ip4dst); 152862306a36Sopenharmony_ci if (err) 152962306a36Sopenharmony_ci goto free_spec; 153062306a36Sopenharmony_ci spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP; 153162306a36Sopenharmony_ci spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src; 153262306a36Sopenharmony_ci spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst; 153362306a36Sopenharmony_ci spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc; 153462306a36Sopenharmony_ci spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst; 153562306a36Sopenharmony_ci } else { 153662306a36Sopenharmony_ci err = mlx4_en_ethtool_add_mac_rule_by_ipv4(priv, cmd, list_h, 153762306a36Sopenharmony_ci spec_l2, 153862306a36Sopenharmony_ci cmd->fs.h_u. 153962306a36Sopenharmony_ci udp_ip4_spec.ip4dst); 154062306a36Sopenharmony_ci if (err) 154162306a36Sopenharmony_ci goto free_spec; 154262306a36Sopenharmony_ci spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP; 154362306a36Sopenharmony_ci spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src; 154462306a36Sopenharmony_ci spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst; 154562306a36Sopenharmony_ci spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc; 154662306a36Sopenharmony_ci spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst; 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci if (l4_mask->ip4src) 155062306a36Sopenharmony_ci spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK; 155162306a36Sopenharmony_ci if (l4_mask->ip4dst) 155262306a36Sopenharmony_ci spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci if (l4_mask->psrc) 155562306a36Sopenharmony_ci spec_l4->tcp_udp.src_port_msk = EN_ETHTOOL_SHORT_MASK; 155662306a36Sopenharmony_ci if (l4_mask->pdst) 155762306a36Sopenharmony_ci spec_l4->tcp_udp.dst_port_msk = EN_ETHTOOL_SHORT_MASK; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci list_add_tail(&spec_l3->list, list_h); 156062306a36Sopenharmony_ci list_add_tail(&spec_l4->list, list_h); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return 0; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cifree_spec: 156562306a36Sopenharmony_ci kfree(spec_l2); 156662306a36Sopenharmony_ci kfree(spec_l3); 156762306a36Sopenharmony_ci kfree(spec_l4); 156862306a36Sopenharmony_ci return err; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev, 157262306a36Sopenharmony_ci struct ethtool_rxnfc *cmd, 157362306a36Sopenharmony_ci struct list_head *rule_list_h) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci int err; 157662306a36Sopenharmony_ci struct ethhdr *eth_spec; 157762306a36Sopenharmony_ci struct mlx4_spec_list *spec_l2; 157862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci err = mlx4_en_validate_flow(dev, cmd); 158162306a36Sopenharmony_ci if (err) 158262306a36Sopenharmony_ci return err; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci switch (cmd->fs.flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) { 158562306a36Sopenharmony_ci case ETHER_FLOW: 158662306a36Sopenharmony_ci spec_l2 = kzalloc(sizeof(*spec_l2), GFP_KERNEL); 158762306a36Sopenharmony_ci if (!spec_l2) 158862306a36Sopenharmony_ci return -ENOMEM; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci eth_spec = &cmd->fs.h_u.ether_spec; 159162306a36Sopenharmony_ci mlx4_en_ethtool_add_mac_rule(cmd, rule_list_h, spec_l2, 159262306a36Sopenharmony_ci ð_spec->h_dest[0]); 159362306a36Sopenharmony_ci spec_l2->eth.ether_type = eth_spec->h_proto; 159462306a36Sopenharmony_ci if (eth_spec->h_proto) 159562306a36Sopenharmony_ci spec_l2->eth.ether_type_enable = 1; 159662306a36Sopenharmony_ci break; 159762306a36Sopenharmony_ci case IP_USER_FLOW: 159862306a36Sopenharmony_ci err = add_ip_rule(priv, cmd, rule_list_h); 159962306a36Sopenharmony_ci break; 160062306a36Sopenharmony_ci case TCP_V4_FLOW: 160162306a36Sopenharmony_ci err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW); 160262306a36Sopenharmony_ci break; 160362306a36Sopenharmony_ci case UDP_V4_FLOW: 160462306a36Sopenharmony_ci err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW); 160562306a36Sopenharmony_ci break; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci return err; 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_cistatic int mlx4_en_flow_replace(struct net_device *dev, 161262306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci int err; 161562306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 161662306a36Sopenharmony_ci struct ethtool_flow_id *loc_rule; 161762306a36Sopenharmony_ci struct mlx4_spec_list *spec, *tmp_spec; 161862306a36Sopenharmony_ci u32 qpn; 161962306a36Sopenharmony_ci u64 reg_id; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci struct mlx4_net_trans_rule rule = { 162262306a36Sopenharmony_ci .queue_mode = MLX4_NET_TRANS_Q_FIFO, 162362306a36Sopenharmony_ci .exclusive = 0, 162462306a36Sopenharmony_ci .allow_loopback = 1, 162562306a36Sopenharmony_ci .promisc_mode = MLX4_FS_REGULAR, 162662306a36Sopenharmony_ci }; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci rule.port = priv->port; 162962306a36Sopenharmony_ci rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location; 163062306a36Sopenharmony_ci INIT_LIST_HEAD(&rule.list); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci /* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */ 163362306a36Sopenharmony_ci if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC) 163462306a36Sopenharmony_ci qpn = priv->drop_qp.qpn; 163562306a36Sopenharmony_ci else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) { 163662306a36Sopenharmony_ci qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1); 163762306a36Sopenharmony_ci } else { 163862306a36Sopenharmony_ci if (cmd->fs.ring_cookie >= priv->rx_ring_num) { 163962306a36Sopenharmony_ci en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist\n", 164062306a36Sopenharmony_ci cmd->fs.ring_cookie); 164162306a36Sopenharmony_ci return -EINVAL; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn; 164462306a36Sopenharmony_ci if (!qpn) { 164562306a36Sopenharmony_ci en_warn(priv, "rxnfc: RX ring (%llu) is inactive\n", 164662306a36Sopenharmony_ci cmd->fs.ring_cookie); 164762306a36Sopenharmony_ci return -EINVAL; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci rule.qpn = qpn; 165162306a36Sopenharmony_ci err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list); 165262306a36Sopenharmony_ci if (err) 165362306a36Sopenharmony_ci goto out_free_list; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci loc_rule = &priv->ethtool_rules[cmd->fs.location]; 165662306a36Sopenharmony_ci if (loc_rule->id) { 165762306a36Sopenharmony_ci err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id); 165862306a36Sopenharmony_ci if (err) { 165962306a36Sopenharmony_ci en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n", 166062306a36Sopenharmony_ci cmd->fs.location, loc_rule->id); 166162306a36Sopenharmony_ci goto out_free_list; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci loc_rule->id = 0; 166462306a36Sopenharmony_ci memset(&loc_rule->flow_spec, 0, 166562306a36Sopenharmony_ci sizeof(struct ethtool_rx_flow_spec)); 166662306a36Sopenharmony_ci list_del(&loc_rule->list); 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci err = mlx4_flow_attach(priv->mdev->dev, &rule, ®_id); 166962306a36Sopenharmony_ci if (err) { 167062306a36Sopenharmony_ci en_err(priv, "Fail to attach network rule at location %d\n", 167162306a36Sopenharmony_ci cmd->fs.location); 167262306a36Sopenharmony_ci goto out_free_list; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci loc_rule->id = reg_id; 167562306a36Sopenharmony_ci memcpy(&loc_rule->flow_spec, &cmd->fs, 167662306a36Sopenharmony_ci sizeof(struct ethtool_rx_flow_spec)); 167762306a36Sopenharmony_ci list_add_tail(&loc_rule->list, &priv->ethtool_list); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ciout_free_list: 168062306a36Sopenharmony_ci list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) { 168162306a36Sopenharmony_ci list_del(&spec->list); 168262306a36Sopenharmony_ci kfree(spec); 168362306a36Sopenharmony_ci } 168462306a36Sopenharmony_ci return err; 168562306a36Sopenharmony_ci} 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_cistatic int mlx4_en_flow_detach(struct net_device *dev, 168862306a36Sopenharmony_ci struct ethtool_rxnfc *cmd) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci int err = 0; 169162306a36Sopenharmony_ci struct ethtool_flow_id *rule; 169262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) 169562306a36Sopenharmony_ci return -EINVAL; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci rule = &priv->ethtool_rules[cmd->fs.location]; 169862306a36Sopenharmony_ci if (!rule->id) { 169962306a36Sopenharmony_ci err = -ENOENT; 170062306a36Sopenharmony_ci goto out; 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci err = mlx4_flow_detach(priv->mdev->dev, rule->id); 170462306a36Sopenharmony_ci if (err) { 170562306a36Sopenharmony_ci en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n", 170662306a36Sopenharmony_ci cmd->fs.location, rule->id); 170762306a36Sopenharmony_ci goto out; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci rule->id = 0; 171062306a36Sopenharmony_ci memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec)); 171162306a36Sopenharmony_ci list_del(&rule->list); 171262306a36Sopenharmony_ciout: 171362306a36Sopenharmony_ci return err; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci} 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_cistatic int mlx4_en_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd, 171862306a36Sopenharmony_ci int loc) 171962306a36Sopenharmony_ci{ 172062306a36Sopenharmony_ci int err = 0; 172162306a36Sopenharmony_ci struct ethtool_flow_id *rule; 172262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES) 172562306a36Sopenharmony_ci return -EINVAL; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci rule = &priv->ethtool_rules[loc]; 172862306a36Sopenharmony_ci if (rule->id) 172962306a36Sopenharmony_ci memcpy(&cmd->fs, &rule->flow_spec, 173062306a36Sopenharmony_ci sizeof(struct ethtool_rx_flow_spec)); 173162306a36Sopenharmony_ci else 173262306a36Sopenharmony_ci err = -ENOENT; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci return err; 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cistatic int mlx4_en_get_num_flows(struct mlx4_en_priv *priv) 173862306a36Sopenharmony_ci{ 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci int i, res = 0; 174162306a36Sopenharmony_ci for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { 174262306a36Sopenharmony_ci if (priv->ethtool_rules[i].id) 174362306a36Sopenharmony_ci res++; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_ci return res; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_cistatic int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, 175062306a36Sopenharmony_ci u32 *rule_locs) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 175362306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 175462306a36Sopenharmony_ci int err = 0; 175562306a36Sopenharmony_ci int i = 0, priority = 0; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT || 175862306a36Sopenharmony_ci cmd->cmd == ETHTOOL_GRXCLSRULE || 175962306a36Sopenharmony_ci cmd->cmd == ETHTOOL_GRXCLSRLALL) && 176062306a36Sopenharmony_ci (mdev->dev->caps.steering_mode != 176162306a36Sopenharmony_ci MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up)) 176262306a36Sopenharmony_ci return -EINVAL; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci switch (cmd->cmd) { 176562306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 176662306a36Sopenharmony_ci cmd->data = priv->rx_ring_num; 176762306a36Sopenharmony_ci break; 176862306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLCNT: 176962306a36Sopenharmony_ci cmd->rule_cnt = mlx4_en_get_num_flows(priv); 177062306a36Sopenharmony_ci break; 177162306a36Sopenharmony_ci case ETHTOOL_GRXCLSRULE: 177262306a36Sopenharmony_ci err = mlx4_en_get_flow(dev, cmd, cmd->fs.location); 177362306a36Sopenharmony_ci break; 177462306a36Sopenharmony_ci case ETHTOOL_GRXCLSRLALL: 177562306a36Sopenharmony_ci cmd->data = MAX_NUM_OF_FS_RULES; 177662306a36Sopenharmony_ci while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) { 177762306a36Sopenharmony_ci err = mlx4_en_get_flow(dev, cmd, i); 177862306a36Sopenharmony_ci if (!err) 177962306a36Sopenharmony_ci rule_locs[priority++] = i; 178062306a36Sopenharmony_ci i++; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci err = 0; 178362306a36Sopenharmony_ci break; 178462306a36Sopenharmony_ci default: 178562306a36Sopenharmony_ci err = -EOPNOTSUPP; 178662306a36Sopenharmony_ci break; 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci return err; 179062306a36Sopenharmony_ci} 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_cistatic int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) 179362306a36Sopenharmony_ci{ 179462306a36Sopenharmony_ci int err = 0; 179562306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 179662306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci if (mdev->dev->caps.steering_mode != 179962306a36Sopenharmony_ci MLX4_STEERING_MODE_DEVICE_MANAGED || !priv->port_up) 180062306a36Sopenharmony_ci return -EINVAL; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci switch (cmd->cmd) { 180362306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLINS: 180462306a36Sopenharmony_ci err = mlx4_en_flow_replace(dev, cmd); 180562306a36Sopenharmony_ci break; 180662306a36Sopenharmony_ci case ETHTOOL_SRXCLSRLDEL: 180762306a36Sopenharmony_ci err = mlx4_en_flow_detach(dev, cmd); 180862306a36Sopenharmony_ci break; 180962306a36Sopenharmony_ci default: 181062306a36Sopenharmony_ci en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd); 181162306a36Sopenharmony_ci return -EINVAL; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci return err; 181562306a36Sopenharmony_ci} 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_cistatic int mlx4_en_get_max_num_rx_rings(struct net_device *dev) 181862306a36Sopenharmony_ci{ 181962306a36Sopenharmony_ci return min_t(int, num_online_cpus(), MAX_RX_RINGS); 182062306a36Sopenharmony_ci} 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_cistatic void mlx4_en_get_channels(struct net_device *dev, 182362306a36Sopenharmony_ci struct ethtool_channels *channel) 182462306a36Sopenharmony_ci{ 182562306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci channel->max_rx = mlx4_en_get_max_num_rx_rings(dev); 182862306a36Sopenharmony_ci channel->max_tx = priv->mdev->profile.max_num_tx_rings_p_up; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci channel->rx_count = priv->rx_ring_num; 183162306a36Sopenharmony_ci channel->tx_count = priv->tx_ring_num[TX] / 183262306a36Sopenharmony_ci priv->prof->num_up; 183362306a36Sopenharmony_ci} 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic int mlx4_en_set_channels(struct net_device *dev, 183662306a36Sopenharmony_ci struct ethtool_channels *channel) 183762306a36Sopenharmony_ci{ 183862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 183962306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 184062306a36Sopenharmony_ci struct mlx4_en_port_profile new_prof; 184162306a36Sopenharmony_ci struct mlx4_en_priv *tmp; 184262306a36Sopenharmony_ci int total_tx_count; 184362306a36Sopenharmony_ci int port_up = 0; 184462306a36Sopenharmony_ci int xdp_count; 184562306a36Sopenharmony_ci int err = 0; 184662306a36Sopenharmony_ci u8 up; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci if (!channel->tx_count || !channel->rx_count) 184962306a36Sopenharmony_ci return -EINVAL; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); 185262306a36Sopenharmony_ci if (!tmp) 185362306a36Sopenharmony_ci return -ENOMEM; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci mutex_lock(&mdev->state_lock); 185662306a36Sopenharmony_ci xdp_count = priv->tx_ring_num[TX_XDP] ? channel->rx_count : 0; 185762306a36Sopenharmony_ci total_tx_count = channel->tx_count * priv->prof->num_up + xdp_count; 185862306a36Sopenharmony_ci if (total_tx_count > MAX_TX_RINGS) { 185962306a36Sopenharmony_ci err = -EINVAL; 186062306a36Sopenharmony_ci en_err(priv, 186162306a36Sopenharmony_ci "Total number of TX and XDP rings (%d) exceeds the maximum supported (%d)\n", 186262306a36Sopenharmony_ci total_tx_count, MAX_TX_RINGS); 186362306a36Sopenharmony_ci goto out; 186462306a36Sopenharmony_ci } 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile)); 186762306a36Sopenharmony_ci new_prof.num_tx_rings_p_up = channel->tx_count; 186862306a36Sopenharmony_ci new_prof.tx_ring_num[TX] = channel->tx_count * priv->prof->num_up; 186962306a36Sopenharmony_ci new_prof.tx_ring_num[TX_XDP] = xdp_count; 187062306a36Sopenharmony_ci new_prof.rx_ring_num = channel->rx_count; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, true); 187362306a36Sopenharmony_ci if (err) 187462306a36Sopenharmony_ci goto out; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci if (priv->port_up) { 187762306a36Sopenharmony_ci port_up = 1; 187862306a36Sopenharmony_ci mlx4_en_stop_port(dev, 1); 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci mlx4_en_safe_replace_resources(priv, tmp); 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci netif_set_real_num_rx_queues(dev, priv->rx_ring_num); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci up = (priv->prof->num_up == MLX4_EN_NUM_UP_LOW) ? 188662306a36Sopenharmony_ci 0 : priv->prof->num_up; 188762306a36Sopenharmony_ci mlx4_en_setup_tc(dev, up); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci en_warn(priv, "Using %d TX rings\n", priv->tx_ring_num[TX]); 189062306a36Sopenharmony_ci en_warn(priv, "Using %d RX rings\n", priv->rx_ring_num); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if (port_up) { 189362306a36Sopenharmony_ci err = mlx4_en_start_port(dev); 189462306a36Sopenharmony_ci if (err) 189562306a36Sopenharmony_ci en_err(priv, "Failed starting port\n"); 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci err = mlx4_en_moderation_update(priv); 189962306a36Sopenharmony_ciout: 190062306a36Sopenharmony_ci mutex_unlock(&mdev->state_lock); 190162306a36Sopenharmony_ci kfree(tmp); 190262306a36Sopenharmony_ci return err; 190362306a36Sopenharmony_ci} 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_cistatic int mlx4_en_get_ts_info(struct net_device *dev, 190662306a36Sopenharmony_ci struct ethtool_ts_info *info) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 190962306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 191062306a36Sopenharmony_ci int ret; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci ret = ethtool_op_get_ts_info(dev, info); 191362306a36Sopenharmony_ci if (ret) 191462306a36Sopenharmony_ci return ret; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) { 191762306a36Sopenharmony_ci info->so_timestamping |= 191862306a36Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 191962306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 192062306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci info->tx_types = 192362306a36Sopenharmony_ci (1 << HWTSTAMP_TX_OFF) | 192462306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci info->rx_filters = 192762306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_NONE) | 192862306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_ALL); 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci if (mdev->ptp_clock) 193162306a36Sopenharmony_ci info->phc_index = ptp_clock_index(mdev->ptp_clock); 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci return ret; 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cistatic int mlx4_en_set_priv_flags(struct net_device *dev, u32 flags) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 194062306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 194162306a36Sopenharmony_ci bool bf_enabled_new = !!(flags & MLX4_EN_PRIV_FLAGS_BLUEFLAME); 194262306a36Sopenharmony_ci bool bf_enabled_old = !!(priv->pflags & MLX4_EN_PRIV_FLAGS_BLUEFLAME); 194362306a36Sopenharmony_ci bool phv_enabled_new = !!(flags & MLX4_EN_PRIV_FLAGS_PHV); 194462306a36Sopenharmony_ci bool phv_enabled_old = !!(priv->pflags & MLX4_EN_PRIV_FLAGS_PHV); 194562306a36Sopenharmony_ci int i; 194662306a36Sopenharmony_ci int ret = 0; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci if (bf_enabled_new != bf_enabled_old) { 194962306a36Sopenharmony_ci int t; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci if (bf_enabled_new) { 195262306a36Sopenharmony_ci bool bf_supported = true; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) 195562306a36Sopenharmony_ci for (i = 0; i < priv->tx_ring_num[t]; i++) 195662306a36Sopenharmony_ci bf_supported &= 195762306a36Sopenharmony_ci priv->tx_ring[t][i]->bf_alloced; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (!bf_supported) { 196062306a36Sopenharmony_ci en_err(priv, "BlueFlame is not supported\n"); 196162306a36Sopenharmony_ci return -EINVAL; 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci priv->pflags |= MLX4_EN_PRIV_FLAGS_BLUEFLAME; 196562306a36Sopenharmony_ci } else { 196662306a36Sopenharmony_ci priv->pflags &= ~MLX4_EN_PRIV_FLAGS_BLUEFLAME; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci for (t = 0; t < MLX4_EN_NUM_TX_TYPES; t++) 197062306a36Sopenharmony_ci for (i = 0; i < priv->tx_ring_num[t]; i++) 197162306a36Sopenharmony_ci priv->tx_ring[t][i]->bf_enabled = 197262306a36Sopenharmony_ci bf_enabled_new; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci en_info(priv, "BlueFlame %s\n", 197562306a36Sopenharmony_ci bf_enabled_new ? "Enabled" : "Disabled"); 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci if (phv_enabled_new != phv_enabled_old) { 197962306a36Sopenharmony_ci ret = set_phv_bit(mdev->dev, priv->port, (int)phv_enabled_new); 198062306a36Sopenharmony_ci if (ret) 198162306a36Sopenharmony_ci return ret; 198262306a36Sopenharmony_ci else if (phv_enabled_new) 198362306a36Sopenharmony_ci priv->pflags |= MLX4_EN_PRIV_FLAGS_PHV; 198462306a36Sopenharmony_ci else 198562306a36Sopenharmony_ci priv->pflags &= ~MLX4_EN_PRIV_FLAGS_PHV; 198662306a36Sopenharmony_ci en_info(priv, "PHV bit %s\n", 198762306a36Sopenharmony_ci phv_enabled_new ? "Enabled" : "Disabled"); 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci return 0; 199062306a36Sopenharmony_ci} 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_cistatic u32 mlx4_en_get_priv_flags(struct net_device *dev) 199362306a36Sopenharmony_ci{ 199462306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci return priv->pflags; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_cistatic int mlx4_en_get_tunable(struct net_device *dev, 200062306a36Sopenharmony_ci const struct ethtool_tunable *tuna, 200162306a36Sopenharmony_ci void *data) 200262306a36Sopenharmony_ci{ 200362306a36Sopenharmony_ci const struct mlx4_en_priv *priv = netdev_priv(dev); 200462306a36Sopenharmony_ci int ret = 0; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci switch (tuna->id) { 200762306a36Sopenharmony_ci case ETHTOOL_TX_COPYBREAK: 200862306a36Sopenharmony_ci *(u32 *)data = priv->prof->inline_thold; 200962306a36Sopenharmony_ci break; 201062306a36Sopenharmony_ci default: 201162306a36Sopenharmony_ci ret = -EINVAL; 201262306a36Sopenharmony_ci break; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci return ret; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic int mlx4_en_set_tunable(struct net_device *dev, 201962306a36Sopenharmony_ci const struct ethtool_tunable *tuna, 202062306a36Sopenharmony_ci const void *data) 202162306a36Sopenharmony_ci{ 202262306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 202362306a36Sopenharmony_ci int val, ret = 0; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci switch (tuna->id) { 202662306a36Sopenharmony_ci case ETHTOOL_TX_COPYBREAK: 202762306a36Sopenharmony_ci val = *(u32 *)data; 202862306a36Sopenharmony_ci if (val < MIN_PKT_LEN || val > MAX_INLINE) 202962306a36Sopenharmony_ci ret = -EINVAL; 203062306a36Sopenharmony_ci else 203162306a36Sopenharmony_ci priv->prof->inline_thold = val; 203262306a36Sopenharmony_ci break; 203362306a36Sopenharmony_ci default: 203462306a36Sopenharmony_ci ret = -EINVAL; 203562306a36Sopenharmony_ci break; 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci return ret; 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_cistatic int mlx4_en_get_module_info(struct net_device *dev, 204262306a36Sopenharmony_ci struct ethtool_modinfo *modinfo) 204362306a36Sopenharmony_ci{ 204462306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 204562306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 204662306a36Sopenharmony_ci int ret; 204762306a36Sopenharmony_ci u8 data[4]; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci /* Read first 2 bytes to get Module & REV ID */ 205062306a36Sopenharmony_ci ret = mlx4_get_module_info(mdev->dev, priv->port, 205162306a36Sopenharmony_ci 0/*offset*/, 2/*size*/, data); 205262306a36Sopenharmony_ci if (ret < 2) 205362306a36Sopenharmony_ci return -EIO; 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci switch (data[0] /* identifier */) { 205662306a36Sopenharmony_ci case MLX4_MODULE_ID_QSFP: 205762306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8436; 205862306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; 205962306a36Sopenharmony_ci break; 206062306a36Sopenharmony_ci case MLX4_MODULE_ID_QSFP_PLUS: 206162306a36Sopenharmony_ci if (data[1] >= 0x3) { /* revision id */ 206262306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8636; 206362306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; 206462306a36Sopenharmony_ci } else { 206562306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8436; 206662306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci break; 206962306a36Sopenharmony_ci case MLX4_MODULE_ID_QSFP28: 207062306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8636; 207162306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN; 207262306a36Sopenharmony_ci break; 207362306a36Sopenharmony_ci case MLX4_MODULE_ID_SFP: 207462306a36Sopenharmony_ci modinfo->type = ETH_MODULE_SFF_8472; 207562306a36Sopenharmony_ci modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; 207662306a36Sopenharmony_ci break; 207762306a36Sopenharmony_ci default: 207862306a36Sopenharmony_ci return -EINVAL; 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci return 0; 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_cistatic int mlx4_en_get_module_eeprom(struct net_device *dev, 208562306a36Sopenharmony_ci struct ethtool_eeprom *ee, 208662306a36Sopenharmony_ci u8 *data) 208762306a36Sopenharmony_ci{ 208862306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 208962306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 209062306a36Sopenharmony_ci int offset = ee->offset; 209162306a36Sopenharmony_ci int i = 0, ret; 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci if (ee->len == 0) 209462306a36Sopenharmony_ci return -EINVAL; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci memset(data, 0, ee->len); 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci while (i < ee->len) { 209962306a36Sopenharmony_ci en_dbg(DRV, priv, 210062306a36Sopenharmony_ci "mlx4_get_module_info i(%d) offset(%d) len(%d)\n", 210162306a36Sopenharmony_ci i, offset, ee->len - i); 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci ret = mlx4_get_module_info(mdev->dev, priv->port, 210462306a36Sopenharmony_ci offset, ee->len - i, data + i); 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci if (!ret) /* Done reading */ 210762306a36Sopenharmony_ci return 0; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci if (ret < 0) { 211062306a36Sopenharmony_ci en_err(priv, 211162306a36Sopenharmony_ci "mlx4_get_module_info i(%d) offset(%d) bytes_to_read(%d) - FAILED (0x%x)\n", 211262306a36Sopenharmony_ci i, offset, ee->len - i, ret); 211362306a36Sopenharmony_ci return ret; 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci i += ret; 211762306a36Sopenharmony_ci offset += ret; 211862306a36Sopenharmony_ci } 211962306a36Sopenharmony_ci return 0; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic int mlx4_en_set_phys_id(struct net_device *dev, 212362306a36Sopenharmony_ci enum ethtool_phys_id_state state) 212462306a36Sopenharmony_ci{ 212562306a36Sopenharmony_ci int err; 212662306a36Sopenharmony_ci u16 beacon_duration; 212762306a36Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 212862306a36Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_BEACON)) 213162306a36Sopenharmony_ci return -EOPNOTSUPP; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci switch (state) { 213462306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 213562306a36Sopenharmony_ci beacon_duration = PORT_BEACON_MAX_LIMIT; 213662306a36Sopenharmony_ci break; 213762306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 213862306a36Sopenharmony_ci beacon_duration = 0; 213962306a36Sopenharmony_ci break; 214062306a36Sopenharmony_ci default: 214162306a36Sopenharmony_ci return -EOPNOTSUPP; 214262306a36Sopenharmony_ci } 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci err = mlx4_SET_PORT_BEACON(mdev->dev, priv->port, beacon_duration); 214562306a36Sopenharmony_ci return err; 214662306a36Sopenharmony_ci} 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ciconst struct ethtool_ops mlx4_en_ethtool_ops = { 214962306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 215062306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES | 215162306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ | 215262306a36Sopenharmony_ci ETHTOOL_COALESCE_PKT_RATE_RX_USECS, 215362306a36Sopenharmony_ci .get_drvinfo = mlx4_en_get_drvinfo, 215462306a36Sopenharmony_ci .get_link_ksettings = mlx4_en_get_link_ksettings, 215562306a36Sopenharmony_ci .set_link_ksettings = mlx4_en_set_link_ksettings, 215662306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 215762306a36Sopenharmony_ci .get_strings = mlx4_en_get_strings, 215862306a36Sopenharmony_ci .get_sset_count = mlx4_en_get_sset_count, 215962306a36Sopenharmony_ci .get_ethtool_stats = mlx4_en_get_ethtool_stats, 216062306a36Sopenharmony_ci .self_test = mlx4_en_self_test, 216162306a36Sopenharmony_ci .set_phys_id = mlx4_en_set_phys_id, 216262306a36Sopenharmony_ci .get_wol = mlx4_en_get_wol, 216362306a36Sopenharmony_ci .set_wol = mlx4_en_set_wol, 216462306a36Sopenharmony_ci .get_msglevel = mlx4_en_get_msglevel, 216562306a36Sopenharmony_ci .set_msglevel = mlx4_en_set_msglevel, 216662306a36Sopenharmony_ci .get_coalesce = mlx4_en_get_coalesce, 216762306a36Sopenharmony_ci .set_coalesce = mlx4_en_set_coalesce, 216862306a36Sopenharmony_ci .get_pause_stats = mlx4_en_get_pause_stats, 216962306a36Sopenharmony_ci .get_pauseparam = mlx4_en_get_pauseparam, 217062306a36Sopenharmony_ci .set_pauseparam = mlx4_en_set_pauseparam, 217162306a36Sopenharmony_ci .get_ringparam = mlx4_en_get_ringparam, 217262306a36Sopenharmony_ci .set_ringparam = mlx4_en_set_ringparam, 217362306a36Sopenharmony_ci .get_rxnfc = mlx4_en_get_rxnfc, 217462306a36Sopenharmony_ci .set_rxnfc = mlx4_en_set_rxnfc, 217562306a36Sopenharmony_ci .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size, 217662306a36Sopenharmony_ci .get_rxfh_key_size = mlx4_en_get_rxfh_key_size, 217762306a36Sopenharmony_ci .get_rxfh = mlx4_en_get_rxfh, 217862306a36Sopenharmony_ci .set_rxfh = mlx4_en_set_rxfh, 217962306a36Sopenharmony_ci .get_channels = mlx4_en_get_channels, 218062306a36Sopenharmony_ci .set_channels = mlx4_en_set_channels, 218162306a36Sopenharmony_ci .get_ts_info = mlx4_en_get_ts_info, 218262306a36Sopenharmony_ci .set_priv_flags = mlx4_en_set_priv_flags, 218362306a36Sopenharmony_ci .get_priv_flags = mlx4_en_get_priv_flags, 218462306a36Sopenharmony_ci .get_tunable = mlx4_en_get_tunable, 218562306a36Sopenharmony_ci .set_tunable = mlx4_en_set_tunable, 218662306a36Sopenharmony_ci .get_module_info = mlx4_en_get_module_info, 218762306a36Sopenharmony_ci .get_module_eeprom = mlx4_en_get_module_eeprom 218862306a36Sopenharmony_ci}; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci 2194