| /* |
| * aQuantia Corporation Network Driver |
| * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| */ |
| |
| /* File aq_ethtool.c: Definition of ethertool related functions. */ |
| |
| #include "aq_ethtool.h" |
| #include "aq_nic.h" |
| |
| static void aq_ethtool_get_regs(struct net_device *ndev, |
| struct ethtool_regs *regs, void *p) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| u32 regs_count = aq_nic_get_regs_count(aq_nic); |
| |
| memset(p, 0, regs_count * sizeof(u32)); |
| aq_nic_get_regs(aq_nic, regs, p); |
| } |
| |
| static int aq_ethtool_get_regs_len(struct net_device *ndev) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| u32 regs_count = aq_nic_get_regs_count(aq_nic); |
| |
| return regs_count * sizeof(u32); |
| } |
| |
| static u32 aq_ethtool_get_link(struct net_device *ndev) |
| { |
| return ethtool_op_get_link(ndev); |
| } |
| |
| static int aq_ethtool_get_link_ksettings(struct net_device *ndev, |
| struct ethtool_link_ksettings *cmd) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| |
| aq_nic_get_link_ksettings(aq_nic, cmd); |
| cmd->base.speed = netif_carrier_ok(ndev) ? |
| aq_nic_get_link_speed(aq_nic) : 0U; |
| |
| return 0; |
| } |
| |
| static int |
| aq_ethtool_set_link_ksettings(struct net_device *ndev, |
| const struct ethtool_link_ksettings *cmd) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| |
| return aq_nic_set_link_ksettings(aq_nic, cmd); |
| } |
| |
| /* there "5U" is number of queue[#] stats lines (InPackets+...+InErrors) */ |
| static const unsigned int aq_ethtool_stat_queue_lines = 5U; |
| static const unsigned int aq_ethtool_stat_queue_chars = |
| 5U * ETH_GSTRING_LEN; |
| static const char aq_ethtool_stat_names[][ETH_GSTRING_LEN] = { |
| "InPackets", |
| "InUCast", |
| "InMCast", |
| "InBCast", |
| "InErrors", |
| "OutPackets", |
| "OutUCast", |
| "OutMCast", |
| "OutBCast", |
| "InUCastOctects", |
| "OutUCastOctects", |
| "InMCastOctects", |
| "OutMCastOctects", |
| "InBCastOctects", |
| "OutBCastOctects", |
| "InOctects", |
| "OutOctects", |
| "InPacketsDma", |
| "OutPacketsDma", |
| "InOctetsDma", |
| "OutOctetsDma", |
| "InDroppedDma", |
| "Queue[0] InPackets", |
| "Queue[0] OutPackets", |
| "Queue[0] InJumboPackets", |
| "Queue[0] InLroPackets", |
| "Queue[0] InErrors", |
| "Queue[1] InPackets", |
| "Queue[1] OutPackets", |
| "Queue[1] InJumboPackets", |
| "Queue[1] InLroPackets", |
| "Queue[1] InErrors", |
| "Queue[2] InPackets", |
| "Queue[2] OutPackets", |
| "Queue[2] InJumboPackets", |
| "Queue[2] InLroPackets", |
| "Queue[2] InErrors", |
| "Queue[3] InPackets", |
| "Queue[3] OutPackets", |
| "Queue[3] InJumboPackets", |
| "Queue[3] InLroPackets", |
| "Queue[3] InErrors", |
| "Queue[4] InPackets", |
| "Queue[4] OutPackets", |
| "Queue[4] InJumboPackets", |
| "Queue[4] InLroPackets", |
| "Queue[4] InErrors", |
| "Queue[5] InPackets", |
| "Queue[5] OutPackets", |
| "Queue[5] InJumboPackets", |
| "Queue[5] InLroPackets", |
| "Queue[5] InErrors", |
| "Queue[6] InPackets", |
| "Queue[6] OutPackets", |
| "Queue[6] InJumboPackets", |
| "Queue[6] InLroPackets", |
| "Queue[6] InErrors", |
| "Queue[7] InPackets", |
| "Queue[7] OutPackets", |
| "Queue[7] InJumboPackets", |
| "Queue[7] InLroPackets", |
| "Queue[7] InErrors", |
| }; |
| |
| static void aq_ethtool_stats(struct net_device *ndev, |
| struct ethtool_stats *stats, u64 *data) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| |
| /* ASSERT: Need add lines to aq_ethtool_stat_names if AQ_CFG_VECS_MAX > 8 */ |
| BUILD_BUG_ON(AQ_CFG_VECS_MAX > 8); |
| memset(data, 0, ARRAY_SIZE(aq_ethtool_stat_names) * sizeof(u64)); |
| aq_nic_get_stats(aq_nic, data); |
| } |
| |
| static void aq_ethtool_get_drvinfo(struct net_device *ndev, |
| struct ethtool_drvinfo *drvinfo) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
| struct pci_dev *pdev = to_pci_dev(ndev->dev.parent); |
| u32 firmware_version = aq_nic_get_fw_version(aq_nic); |
| u32 regs_count = aq_nic_get_regs_count(aq_nic); |
| |
| strlcat(drvinfo->driver, AQ_CFG_DRV_NAME, sizeof(drvinfo->driver)); |
| strlcat(drvinfo->version, AQ_CFG_DRV_VERSION, sizeof(drvinfo->version)); |
| |
| snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), |
| "%u.%u.%u", firmware_version >> 24, |
| (firmware_version >> 16) & 0xFFU, firmware_version & 0xFFFFU); |
| |
| strlcpy(drvinfo->bus_info, pdev ? pci_name(pdev) : "", |
| sizeof(drvinfo->bus_info)); |
| drvinfo->n_stats = ARRAY_SIZE(aq_ethtool_stat_names) - |
| (AQ_CFG_VECS_MAX - cfg->vecs) * aq_ethtool_stat_queue_lines; |
| drvinfo->testinfo_len = 0; |
| drvinfo->regdump_len = regs_count; |
| drvinfo->eedump_len = 0; |
| } |
| |
| static void aq_ethtool_get_strings(struct net_device *ndev, |
| u32 stringset, u8 *data) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
| |
| if (stringset == ETH_SS_STATS) |
| memcpy(data, *aq_ethtool_stat_names, |
| sizeof(aq_ethtool_stat_names) - |
| (AQ_CFG_VECS_MAX - cfg->vecs) * |
| aq_ethtool_stat_queue_chars); |
| } |
| |
| static int aq_ethtool_get_sset_count(struct net_device *ndev, int stringset) |
| { |
| int ret = 0; |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
| |
| switch (stringset) { |
| case ETH_SS_STATS: |
| ret = ARRAY_SIZE(aq_ethtool_stat_names) - |
| (AQ_CFG_VECS_MAX - cfg->vecs) * |
| aq_ethtool_stat_queue_lines; |
| break; |
| default: |
| ret = -EOPNOTSUPP; |
| } |
| return ret; |
| } |
| |
| static u32 aq_ethtool_get_rss_indir_size(struct net_device *ndev) |
| { |
| return AQ_CFG_RSS_INDIRECTION_TABLE_MAX; |
| } |
| |
| static u32 aq_ethtool_get_rss_key_size(struct net_device *ndev) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
| |
| return sizeof(cfg->aq_rss.hash_secret_key); |
| } |
| |
| static int aq_ethtool_get_rss(struct net_device *ndev, u32 *indir, u8 *key, |
| u8 *hfunc) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
| unsigned int i = 0U; |
| |
| if (hfunc) |
| *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ |
| if (indir) { |
| for (i = 0; i < AQ_CFG_RSS_INDIRECTION_TABLE_MAX; i++) |
| indir[i] = cfg->aq_rss.indirection_table[i]; |
| } |
| if (key) |
| memcpy(key, cfg->aq_rss.hash_secret_key, |
| sizeof(cfg->aq_rss.hash_secret_key)); |
| return 0; |
| } |
| |
| static int aq_ethtool_get_rxnfc(struct net_device *ndev, |
| struct ethtool_rxnfc *cmd, |
| u32 *rule_locs) |
| { |
| struct aq_nic_s *aq_nic = netdev_priv(ndev); |
| struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); |
| int err = 0; |
| |
| switch (cmd->cmd) { |
| case ETHTOOL_GRXRINGS: |
| cmd->data = cfg->vecs; |
| break; |
| |
| default: |
| err = -EOPNOTSUPP; |
| break; |
| } |
| |
| return err; |
| } |
| |
| const struct ethtool_ops aq_ethtool_ops = { |
| .get_link = aq_ethtool_get_link, |
| .get_regs_len = aq_ethtool_get_regs_len, |
| .get_regs = aq_ethtool_get_regs, |
| .get_drvinfo = aq_ethtool_get_drvinfo, |
| .get_strings = aq_ethtool_get_strings, |
| .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size, |
| .get_rxfh_key_size = aq_ethtool_get_rss_key_size, |
| .get_rxfh = aq_ethtool_get_rss, |
| .get_rxnfc = aq_ethtool_get_rxnfc, |
| .get_sset_count = aq_ethtool_get_sset_count, |
| .get_ethtool_stats = aq_ethtool_stats, |
| .get_link_ksettings = aq_ethtool_get_link_ksettings, |
| .set_link_ksettings = aq_ethtool_set_link_ksettings, |
| }; |