/* * 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 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, };