diff options
Diffstat (limited to 'target/linux/ipq40xx/files-5.4/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c')
-rw-r--r-- | target/linux/ipq40xx/files-5.4/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/target/linux/ipq40xx/files-5.4/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c b/target/linux/ipq40xx/files-5.4/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c new file mode 100644 index 0000000000..8f6e3833b6 --- /dev/null +++ b/target/linux/ipq40xx/files-5.4/drivers/net/ethernet/qualcomm/essedma/edma_ethtool.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all copies. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/ethtool.h> +#include <linux/netdevice.h> +#include <linux/string.h> +#include "edma.h" + +struct edma_ethtool_stats { + uint8_t stat_string[ETH_GSTRING_LEN]; + uint32_t stat_offset; +}; + +#define EDMA_STAT(m) offsetof(struct edma_ethtool_statistics, m) +#define DRVINFO_LEN 32 + +/* Array of strings describing statistics + */ +static const struct edma_ethtool_stats edma_gstrings_stats[] = { + {"tx_q0_pkt", EDMA_STAT(tx_q0_pkt)}, + {"tx_q1_pkt", EDMA_STAT(tx_q1_pkt)}, + {"tx_q2_pkt", EDMA_STAT(tx_q2_pkt)}, + {"tx_q3_pkt", EDMA_STAT(tx_q3_pkt)}, + {"tx_q4_pkt", EDMA_STAT(tx_q4_pkt)}, + {"tx_q5_pkt", EDMA_STAT(tx_q5_pkt)}, + {"tx_q6_pkt", EDMA_STAT(tx_q6_pkt)}, + {"tx_q7_pkt", EDMA_STAT(tx_q7_pkt)}, + {"tx_q8_pkt", EDMA_STAT(tx_q8_pkt)}, + {"tx_q9_pkt", EDMA_STAT(tx_q9_pkt)}, + {"tx_q10_pkt", EDMA_STAT(tx_q10_pkt)}, + {"tx_q11_pkt", EDMA_STAT(tx_q11_pkt)}, + {"tx_q12_pkt", EDMA_STAT(tx_q12_pkt)}, + {"tx_q13_pkt", EDMA_STAT(tx_q13_pkt)}, + {"tx_q14_pkt", EDMA_STAT(tx_q14_pkt)}, + {"tx_q15_pkt", EDMA_STAT(tx_q15_pkt)}, + {"tx_q0_byte", EDMA_STAT(tx_q0_byte)}, + {"tx_q1_byte", EDMA_STAT(tx_q1_byte)}, + {"tx_q2_byte", EDMA_STAT(tx_q2_byte)}, + {"tx_q3_byte", EDMA_STAT(tx_q3_byte)}, + {"tx_q4_byte", EDMA_STAT(tx_q4_byte)}, + {"tx_q5_byte", EDMA_STAT(tx_q5_byte)}, + {"tx_q6_byte", EDMA_STAT(tx_q6_byte)}, + {"tx_q7_byte", EDMA_STAT(tx_q7_byte)}, + {"tx_q8_byte", EDMA_STAT(tx_q8_byte)}, + {"tx_q9_byte", EDMA_STAT(tx_q9_byte)}, + {"tx_q10_byte", EDMA_STAT(tx_q10_byte)}, + {"tx_q11_byte", EDMA_STAT(tx_q11_byte)}, + {"tx_q12_byte", EDMA_STAT(tx_q12_byte)}, + {"tx_q13_byte", EDMA_STAT(tx_q13_byte)}, + {"tx_q14_byte", EDMA_STAT(tx_q14_byte)}, + {"tx_q15_byte", EDMA_STAT(tx_q15_byte)}, + {"rx_q0_pkt", EDMA_STAT(rx_q0_pkt)}, + {"rx_q1_pkt", EDMA_STAT(rx_q1_pkt)}, + {"rx_q2_pkt", EDMA_STAT(rx_q2_pkt)}, + {"rx_q3_pkt", EDMA_STAT(rx_q3_pkt)}, + {"rx_q4_pkt", EDMA_STAT(rx_q4_pkt)}, + {"rx_q5_pkt", EDMA_STAT(rx_q5_pkt)}, + {"rx_q6_pkt", EDMA_STAT(rx_q6_pkt)}, + {"rx_q7_pkt", EDMA_STAT(rx_q7_pkt)}, + {"rx_q0_byte", EDMA_STAT(rx_q0_byte)}, + {"rx_q1_byte", EDMA_STAT(rx_q1_byte)}, + {"rx_q2_byte", EDMA_STAT(rx_q2_byte)}, + {"rx_q3_byte", EDMA_STAT(rx_q3_byte)}, + {"rx_q4_byte", EDMA_STAT(rx_q4_byte)}, + {"rx_q5_byte", EDMA_STAT(rx_q5_byte)}, + {"rx_q6_byte", EDMA_STAT(rx_q6_byte)}, + {"rx_q7_byte", EDMA_STAT(rx_q7_byte)}, + {"tx_desc_error", EDMA_STAT(tx_desc_error)}, + {"rx_alloc_fail_ctr", EDMA_STAT(rx_alloc_fail_ctr)}, +}; + +#define EDMA_STATS_LEN ARRAY_SIZE(edma_gstrings_stats) + +/* edma_get_strset_count() + * Get strset count + */ +static int edma_get_strset_count(struct net_device *netdev, + int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return EDMA_STATS_LEN; + default: + netdev_dbg(netdev, "%s: Invalid string set", __func__); + return -EOPNOTSUPP; + } +} + + +/* edma_get_strings() + * get stats string + */ +static void edma_get_strings(struct net_device *netdev, uint32_t stringset, + uint8_t *data) +{ + uint8_t *p = data; + uint32_t i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < EDMA_STATS_LEN; i++) { + memcpy(p, edma_gstrings_stats[i].stat_string, + min((size_t)ETH_GSTRING_LEN, + strlen(edma_gstrings_stats[i].stat_string) + + 1)); + p += ETH_GSTRING_LEN; + } + break; + } +} + +/* edma_get_ethtool_stats() + * Get ethtool statistics + */ +static void edma_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, uint64_t *data) +{ + struct edma_adapter *adapter = netdev_priv(netdev); + struct edma_common_info *edma_cinfo = adapter->edma_cinfo; + int i; + uint8_t *p = NULL; + + edma_read_append_stats(edma_cinfo); + + for(i = 0; i < EDMA_STATS_LEN; i++) { + p = (uint8_t *)&(edma_cinfo->edma_ethstats) + + edma_gstrings_stats[i].stat_offset; + data[i] = *(uint32_t *)p; + } +} + +/* edma_get_drvinfo() + * get edma driver info + */ +static void edma_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, "ess_edma", DRVINFO_LEN); + strlcpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN); +} + +/* edma_nway_reset() + * Reset the phy, if available. + */ +static int edma_nway_reset(struct net_device *netdev) +{ + return -EINVAL; +} + +/* edma_get_wol() + * get wake on lan info + */ +static void edma_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + wol->supported = 0; + wol->wolopts = 0; +} + +/* edma_get_msglevel() + * get message level. + */ +static uint32_t edma_get_msglevel(struct net_device *netdev) +{ + return 0; +} + +/* edma_get_settings() + * Get edma settings + */ +#if 0 +static int edma_get_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct edma_adapter *adapter = netdev_priv(netdev); + + if (adapter->poll_required) { + struct phy_device *phydev = NULL; + uint16_t phyreg; + + if ((adapter->forced_speed != SPEED_UNKNOWN) + && !(adapter->poll_required)) + return -EPERM; + + phydev = adapter->phydev; + + ecmd->advertising = phydev->advertising; + ecmd->autoneg = phydev->autoneg; + + if (adapter->link_state == __EDMA_LINKDOWN) { + ecmd->speed = SPEED_UNKNOWN; + ecmd->duplex = DUPLEX_UNKNOWN; + } else { + ecmd->speed = phydev->speed; + ecmd->duplex = phydev->duplex; + } + + ecmd->phy_address = adapter->phy_mdio_addr; + + phyreg = (uint16_t)phy_read(adapter->phydev, MII_LPA); + if (phyreg & LPA_10HALF) + ecmd->lp_advertising |= ADVERTISED_10baseT_Half; + + if (phyreg & LPA_10FULL) + ecmd->lp_advertising |= ADVERTISED_10baseT_Full; + + if (phyreg & LPA_100HALF) + ecmd->lp_advertising |= ADVERTISED_100baseT_Half; + + if (phyreg & LPA_100FULL) + ecmd->lp_advertising |= ADVERTISED_100baseT_Full; + + phyreg = (uint16_t)phy_read(adapter->phydev, MII_STAT1000); + if (phyreg & LPA_1000HALF) + ecmd->lp_advertising |= ADVERTISED_1000baseT_Half; + + if (phyreg & LPA_1000FULL) + ecmd->lp_advertising |= ADVERTISED_1000baseT_Full; + } else { + /* If the speed/duplex for this GMAC is forced and we + * are not polling for link state changes, return the + * values as specified by platform. This will be true + * for GMACs connected to switch, and interfaces that + * do not use a PHY. + */ + if (!(adapter->poll_required)) { + if (adapter->forced_speed != SPEED_UNKNOWN) { + /* set speed and duplex */ + ethtool_cmd_speed_set(ecmd, SPEED_1000); + ecmd->duplex = DUPLEX_FULL; + + /* Populate capabilities advertised by self */ + ecmd->advertising = 0; + ecmd->autoneg = 0; + ecmd->port = PORT_TP; + ecmd->transceiver = XCVR_EXTERNAL; + } else { + /* non link polled and non + * forced speed/duplex interface + */ + return -EIO; + } + } + } + + return 0; +} + +/* edma_set_settings() + * Set EDMA settings + */ +static int edma_set_settings(struct net_device *netdev, + struct ethtool_cmd *ecmd) +{ + struct edma_adapter *adapter = netdev_priv(netdev); + struct phy_device *phydev = NULL; + + if ((adapter->forced_speed != SPEED_UNKNOWN) && + !adapter->poll_required) + return -EPERM; + + phydev = adapter->phydev; + phydev->advertising = ecmd->advertising; + phydev->autoneg = ecmd->autoneg; + phydev->speed = ethtool_cmd_speed(ecmd); + phydev->duplex = ecmd->duplex; + + genphy_config_aneg(phydev); + + return 0; +} +#endif + +/* edma_get_coalesce + * get interrupt mitigation + */ +static int edma_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + u32 reg_val; + + edma_get_tx_rx_coalesce(®_val); + + /* We read the Interrupt Moderation Timer(IMT) register value, + * use lower 16 bit for rx and higher 16 bit for Tx. We do a + * left shift by 1, because IMT resolution timer is 2usecs. + * Hence the value given by the register is multiplied by 2 to + * get the actual time in usecs. + */ + ec->tx_coalesce_usecs = (((reg_val >> 16) & 0xffff) << 1); + ec->rx_coalesce_usecs = ((reg_val & 0xffff) << 1); + + return 0; +} + +/* edma_set_coalesce + * set interrupt mitigation + */ +static int edma_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + if (ec->tx_coalesce_usecs) + edma_change_tx_coalesce(ec->tx_coalesce_usecs); + if (ec->rx_coalesce_usecs) + edma_change_rx_coalesce(ec->rx_coalesce_usecs); + + return 0; +} + +/* edma_set_priv_flags() + * Set EDMA private flags + */ +static int edma_set_priv_flags(struct net_device *netdev, u32 flags) +{ + return 0; +} + +/* edma_get_priv_flags() + * get edma driver flags + */ +static u32 edma_get_priv_flags(struct net_device *netdev) +{ + return 0; +} + +/* edma_get_ringparam() + * get ring size + */ +static void edma_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct edma_adapter *adapter = netdev_priv(netdev); + struct edma_common_info *edma_cinfo = adapter->edma_cinfo; + + ring->tx_max_pending = edma_cinfo->tx_ring_count; + ring->rx_max_pending = edma_cinfo->rx_ring_count; +} + +/* Ethtool operations + */ +static const struct ethtool_ops edma_ethtool_ops = { + .get_drvinfo = &edma_get_drvinfo, + .get_link = ðtool_op_get_link, + .get_msglevel = &edma_get_msglevel, + .nway_reset = &edma_nway_reset, + .get_wol = &edma_get_wol, +// .get_settings = &edma_get_settings, +// .set_settings = &edma_set_settings, + .get_strings = &edma_get_strings, + .get_sset_count = &edma_get_strset_count, + .get_ethtool_stats = &edma_get_ethtool_stats, + .get_coalesce = &edma_get_coalesce, + .set_coalesce = &edma_set_coalesce, + .get_priv_flags = edma_get_priv_flags, + .set_priv_flags = edma_set_priv_flags, + .get_ringparam = edma_get_ringparam, +}; + +/* edma_set_ethtool_ops + * Set ethtool operations + */ +void edma_set_ethtool_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &edma_ethtool_ops; +} |