aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/benet/be_ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/benet/be_ethtool.c')
-rw-r--r--drivers/staging/benet/be_ethtool.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/drivers/staging/benet/be_ethtool.c b/drivers/staging/benet/be_ethtool.c
new file mode 100644
index 000000000000..027af85707aa
--- /dev/null
+++ b/drivers/staging/benet/be_ethtool.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2005 - 2008 ServerEngines
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation. The full GNU General
+ * Public License is included in this distribution in the file called COPYING.
+ *
+ * Contact Information:
+ * linux-drivers@serverengines.com
+ *
+ * ServerEngines
+ * 209 N. Fair Oaks Ave
+ * Sunnyvale, CA 94085
+ */
+/*
+ * be_ethtool.c
+ *
+ * This file contains various functions that ethtool can use
+ * to talk to the driver and the BE H/W.
+ */
+
+#include "benet.h"
+
+#include <linux/ethtool.h>
+
+static const char benet_gstrings_stats[][ETH_GSTRING_LEN] = {
+/* net_device_stats */
+ "rx_packets",
+ "tx_packets",
+ "rx_bytes",
+ "tx_bytes",
+ "rx_errors",
+ "tx_errors",
+ "rx_dropped",
+ "tx_dropped",
+ "multicast",
+ "collisions",
+ "rx_length_errors",
+ "rx_over_errors",
+ "rx_crc_errors",
+ "rx_frame_errors",
+ "rx_fifo_errors",
+ "rx_missed_errors",
+ "tx_aborted_errors",
+ "tx_carrier_errors",
+ "tx_fifo_errors",
+ "tx_heartbeat_errors",
+ "tx_window_errors",
+ "rx_compressed",
+ "tc_compressed",
+/* BE driver Stats */
+ "bes_tx_reqs",
+ "bes_tx_fails",
+ "bes_fwd_reqs",
+ "bes_tx_wrbs",
+ "bes_interrupts",
+ "bes_events",
+ "bes_tx_events",
+ "bes_rx_events",
+ "bes_tx_compl",
+ "bes_rx_compl",
+ "bes_ethrx_post_fail",
+ "bes_802_3_dropped_frames",
+ "bes_802_3_malformed_frames",
+ "bes_rx_misc_pkts",
+ "bes_eth_tx_rate",
+ "bes_eth_rx_rate",
+ "Num Packets collected",
+ "Num Times Flushed",
+};
+
+#define NET_DEV_STATS_LEN \
+ (sizeof(struct net_device_stats)/sizeof(unsigned long))
+
+#define BENET_STATS_LEN ARRAY_SIZE(benet_gstrings_stats)
+
+static void
+be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ struct be_adapter *adapter = pnob->adapter;
+
+ strncpy(drvinfo->driver, be_driver_name, 32);
+ strncpy(drvinfo->version, be_drvr_ver, 32);
+ strncpy(drvinfo->fw_version, be_fw_ver, 32);
+ strcpy(drvinfo->bus_info, pci_name(adapter->pdev));
+ drvinfo->testinfo_len = 0;
+ drvinfo->regdump_len = 0;
+ drvinfo->eedump_len = 0;
+}
+
+static int
+be_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ struct be_adapter *adapter = pnob->adapter;
+
+ coalesce->rx_max_coalesced_frames = adapter->max_rx_coal;
+
+ coalesce->rx_coalesce_usecs = adapter->cur_eqd;
+ coalesce->rx_coalesce_usecs_high = adapter->max_eqd;
+ coalesce->rx_coalesce_usecs_low = adapter->min_eqd;
+
+ coalesce->tx_coalesce_usecs = adapter->cur_eqd;
+ coalesce->tx_coalesce_usecs_high = adapter->max_eqd;
+ coalesce->tx_coalesce_usecs_low = adapter->min_eqd;
+
+ coalesce->use_adaptive_rx_coalesce = adapter->enable_aic;
+ coalesce->use_adaptive_tx_coalesce = adapter->enable_aic;
+
+ return 0;
+}
+
+/*
+ * This routine is used to set interrup coalescing delay *as well as*
+ * the number of pkts to coalesce for LRO.
+ */
+static int
+be_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ struct be_adapter *adapter = pnob->adapter;
+ struct be_eq_object *eq_objectp;
+ u32 max, min, cur;
+ int status;
+
+ adapter->max_rx_coal = coalesce->rx_max_coalesced_frames;
+ if (adapter->max_rx_coal >= BE_LRO_MAX_PKTS)
+ adapter->max_rx_coal = BE_LRO_MAX_PKTS;
+
+ if (adapter->enable_aic == 0 &&
+ coalesce->use_adaptive_rx_coalesce == 1) {
+ /* if AIC is being turned on now, start with an EQD of 0 */
+ adapter->cur_eqd = 0;
+ }
+ adapter->enable_aic = coalesce->use_adaptive_rx_coalesce;
+
+ /* round off to nearest multiple of 8 */
+ max = (((coalesce->rx_coalesce_usecs_high + 4) >> 3) << 3);
+ min = (((coalesce->rx_coalesce_usecs_low + 4) >> 3) << 3);
+ cur = (((coalesce->rx_coalesce_usecs + 4) >> 3) << 3);
+
+ if (adapter->enable_aic) {
+ /* accept low and high if AIC is enabled */
+ if (max > MAX_EQD)
+ max = MAX_EQD;
+ if (min > max)
+ min = max;
+ adapter->max_eqd = max;
+ adapter->min_eqd = min;
+ if (adapter->cur_eqd > max)
+ adapter->cur_eqd = max;
+ if (adapter->cur_eqd < min)
+ adapter->cur_eqd = min;
+ } else {
+ /* accept specified coalesce_usecs only if AIC is disabled */
+ if (cur > MAX_EQD)
+ cur = MAX_EQD;
+ eq_objectp = &pnob->event_q_obj;
+ status =
+ be_eq_modify_delay(&pnob->fn_obj, 1, &eq_objectp, &cur,
+ NULL, NULL, NULL);
+ if (status == BE_SUCCESS)
+ adapter->cur_eqd = cur;
+ }
+ return 0;
+}
+
+static u32 be_get_rx_csum(struct net_device *netdev)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ struct be_adapter *adapter = pnob->adapter;
+ return adapter->rx_csum;
+}
+
+static int be_set_rx_csum(struct net_device *netdev, uint32_t data)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ struct be_adapter *adapter = pnob->adapter;
+
+ if (data)
+ adapter->rx_csum = 1;
+ else
+ adapter->rx_csum = 0;
+
+ return 0;
+}
+
+static void
+be_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *benet_gstrings_stats,
+ sizeof(benet_gstrings_stats));
+ break;
+ }
+}
+
+static int be_get_stats_count(struct net_device *netdev)
+{
+ return BENET_STATS_LEN;
+}
+
+static void
+be_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, uint64_t *data)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ struct be_adapter *adapter = pnob->adapter;
+ int i;
+
+ benet_get_stats(netdev);
+
+ for (i = 0; i <= NET_DEV_STATS_LEN; i++)
+ data[i] = ((unsigned long *)&adapter->benet_stats)[i];
+
+ data[i] = adapter->be_stat.bes_tx_reqs;
+ data[i++] = adapter->be_stat.bes_tx_fails;
+ data[i++] = adapter->be_stat.bes_fwd_reqs;
+ data[i++] = adapter->be_stat.bes_tx_wrbs;
+
+ data[i++] = adapter->be_stat.bes_ints;
+ data[i++] = adapter->be_stat.bes_events;
+ data[i++] = adapter->be_stat.bes_tx_events;
+ data[i++] = adapter->be_stat.bes_rx_events;
+ data[i++] = adapter->be_stat.bes_tx_compl;
+ data[i++] = adapter->be_stat.bes_rx_compl;
+ data[i++] = adapter->be_stat.bes_ethrx_post_fail;
+ data[i++] = adapter->be_stat.bes_802_3_dropped_frames;
+ data[i++] = adapter->be_stat.bes_802_3_malformed_frames;
+ data[i++] = adapter->be_stat.bes_rx_misc_pkts;
+ data[i++] = adapter->be_stat.bes_eth_tx_rate;
+ data[i++] = adapter->be_stat.bes_eth_rx_rate;
+ data[i++] = adapter->be_stat.bes_rx_coal;
+ data[i++] = adapter->be_stat.bes_rx_flush;
+
+}
+
+static int be_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
+{
+ ecmd->speed = SPEED_10000;
+ ecmd->duplex = DUPLEX_FULL;
+ ecmd->autoneg = AUTONEG_DISABLE;
+ return 0;
+}
+
+/* Get the Ring parameters from the pnob */
+static void
+be_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+
+ /* Pre Set Maxims */
+ ring->rx_max_pending = pnob->rx_q_len;
+ ring->rx_mini_max_pending = ring->rx_mini_max_pending;
+ ring->rx_jumbo_max_pending = ring->rx_jumbo_max_pending;
+ ring->tx_max_pending = pnob->tx_q_len;
+
+ /* Current hardware Settings */
+ ring->rx_pending = atomic_read(&pnob->rx_q_posted);
+ ring->rx_mini_pending = ring->rx_mini_pending;
+ ring->rx_jumbo_pending = ring->rx_jumbo_pending;
+ ring->tx_pending = atomic_read(&pnob->tx_q_used);
+
+}
+
+static void
+be_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ bool rxfc, txfc;
+ int status;
+
+ status = be_eth_get_flow_control(&pnob->fn_obj, &txfc, &rxfc);
+ if (status != BE_SUCCESS) {
+ dev_info(&netdev->dev, "Unable to get pause frame settings\n");
+ /* return defaults */
+ ecmd->rx_pause = 1;
+ ecmd->tx_pause = 0;
+ ecmd->autoneg = AUTONEG_ENABLE;
+ return;
+ }
+
+ if (txfc == true)
+ ecmd->tx_pause = 1;
+ else
+ ecmd->tx_pause = 0;
+
+ if (rxfc == true)
+ ecmd->rx_pause = 1;
+ else
+ ecmd->rx_pause = 0;
+
+ ecmd->autoneg = AUTONEG_ENABLE;
+}
+
+static int
+be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd)
+{
+ struct be_net_object *pnob = netdev_priv(netdev);
+ bool txfc, rxfc;
+ int status;
+
+ if (ecmd->autoneg != AUTONEG_ENABLE)
+ return -EINVAL;
+
+ if (ecmd->tx_pause)
+ txfc = true;
+ else
+ txfc = false;
+
+ if (ecmd->rx_pause)
+ rxfc = true;
+ else
+ rxfc = false;
+
+ status = be_eth_set_flow_control(&pnob->fn_obj, txfc, rxfc);
+ if (status != BE_SUCCESS) {
+ dev_info(&netdev->dev, "Unable to set pause frame settings\n");
+ return -1;
+ }
+ return 0;
+}
+
+struct ethtool_ops be_ethtool_ops = {
+ .get_settings = be_get_settings,
+ .get_drvinfo = be_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = be_get_coalesce,
+ .set_coalesce = be_set_coalesce,
+ .get_ringparam = be_get_ringparam,
+ .get_pauseparam = be_get_pauseparam,
+ .set_pauseparam = be_set_pauseparam,
+ .get_rx_csum = be_get_rx_csum,
+ .set_rx_csum = be_set_rx_csum,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+ .get_strings = be_get_strings,
+ .get_stats_count = be_get_stats_count,
+ .get_ethtool_stats = be_get_ethtool_stats,
+};