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