linux/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * DPAA2 Ethernet Switch ethtool support
   4 *
   5 * Copyright 2014-2016 Freescale Semiconductor Inc.
   6 * Copyright 2017-2018 NXP
   7 *
   8 */
   9
  10#include "ethsw.h"
  11
  12static struct {
  13        enum dpsw_counter id;
  14        char name[ETH_GSTRING_LEN];
  15} ethsw_ethtool_counters[] =  {
  16        {DPSW_CNT_ING_FRAME,            "rx frames"},
  17        {DPSW_CNT_ING_BYTE,             "rx bytes"},
  18        {DPSW_CNT_ING_FLTR_FRAME,       "rx filtered frames"},
  19        {DPSW_CNT_ING_FRAME_DISCARD,    "rx discarded frames"},
  20        {DPSW_CNT_ING_BCAST_FRAME,      "rx b-cast frames"},
  21        {DPSW_CNT_ING_BCAST_BYTES,      "rx b-cast bytes"},
  22        {DPSW_CNT_ING_MCAST_FRAME,      "rx m-cast frames"},
  23        {DPSW_CNT_ING_MCAST_BYTE,       "rx m-cast bytes"},
  24        {DPSW_CNT_EGR_FRAME,            "tx frames"},
  25        {DPSW_CNT_EGR_BYTE,             "tx bytes"},
  26        {DPSW_CNT_EGR_FRAME_DISCARD,    "tx discarded frames"},
  27
  28};
  29
  30#define ETHSW_NUM_COUNTERS      ARRAY_SIZE(ethsw_ethtool_counters)
  31
  32static void ethsw_get_drvinfo(struct net_device *netdev,
  33                              struct ethtool_drvinfo *drvinfo)
  34{
  35        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
  36        u16 version_major, version_minor;
  37        int err;
  38
  39        strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
  40
  41        err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
  42                                   &version_major,
  43                                   &version_minor);
  44        if (err)
  45                strlcpy(drvinfo->fw_version, "N/A",
  46                        sizeof(drvinfo->fw_version));
  47        else
  48                snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
  49                         "%u.%u", version_major, version_minor);
  50
  51        strlcpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
  52                sizeof(drvinfo->bus_info));
  53}
  54
  55static int
  56ethsw_get_link_ksettings(struct net_device *netdev,
  57                         struct ethtool_link_ksettings *link_ksettings)
  58{
  59        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
  60        struct dpsw_link_state state = {0};
  61        int err = 0;
  62
  63        err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
  64                                     port_priv->ethsw_data->dpsw_handle,
  65                                     port_priv->idx,
  66                                     &state);
  67        if (err) {
  68                netdev_err(netdev, "ERROR %d getting link state", err);
  69                goto out;
  70        }
  71
  72        /* At the moment, we have no way of interrogating the DPMAC
  73         * from the DPSW side or there may not exist a DPMAC at all.
  74         * Report only autoneg state, duplexity and speed.
  75         */
  76        if (state.options & DPSW_LINK_OPT_AUTONEG)
  77                link_ksettings->base.autoneg = AUTONEG_ENABLE;
  78        if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
  79                link_ksettings->base.duplex = DUPLEX_FULL;
  80        link_ksettings->base.speed = state.rate;
  81
  82out:
  83        return err;
  84}
  85
  86static int
  87ethsw_set_link_ksettings(struct net_device *netdev,
  88                         const struct ethtool_link_ksettings *link_ksettings)
  89{
  90        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
  91        struct dpsw_link_cfg cfg = {0};
  92        int err = 0;
  93
  94        netdev_dbg(netdev, "Setting link parameters...");
  95
  96        /* Due to a temporary MC limitation, the DPSW port must be down
  97         * in order to be able to change link settings. Taking steps to let
  98         * the user know that.
  99         */
 100        if (netif_running(netdev)) {
 101                netdev_info(netdev, "Sorry, interface must be brought down first.\n");
 102                return -EACCES;
 103        }
 104
 105        cfg.rate = link_ksettings->base.speed;
 106        if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
 107                cfg.options |= DPSW_LINK_OPT_AUTONEG;
 108        else
 109                cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
 110        if (link_ksettings->base.duplex  == DUPLEX_HALF)
 111                cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
 112        else
 113                cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
 114
 115        err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
 116                                   port_priv->ethsw_data->dpsw_handle,
 117                                   port_priv->idx,
 118                                   &cfg);
 119        if (err)
 120                /* ethtool will be loud enough if we return an error; no point
 121                 * in putting our own error message on the console by default
 122                 */
 123                netdev_dbg(netdev, "ERROR %d setting link cfg", err);
 124
 125        return err;
 126}
 127
 128static int ethsw_ethtool_get_sset_count(struct net_device *dev, int sset)
 129{
 130        switch (sset) {
 131        case ETH_SS_STATS:
 132                return ETHSW_NUM_COUNTERS;
 133        default:
 134                return -EOPNOTSUPP;
 135        }
 136}
 137
 138static void ethsw_ethtool_get_strings(struct net_device *netdev,
 139                                      u32 stringset, u8 *data)
 140{
 141        int i;
 142
 143        switch (stringset) {
 144        case ETH_SS_STATS:
 145                for (i = 0; i < ETHSW_NUM_COUNTERS; i++)
 146                        memcpy(data + i * ETH_GSTRING_LEN,
 147                               ethsw_ethtool_counters[i].name, ETH_GSTRING_LEN);
 148                break;
 149        }
 150}
 151
 152static void ethsw_ethtool_get_stats(struct net_device *netdev,
 153                                    struct ethtool_stats *stats,
 154                                    u64 *data)
 155{
 156        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 157        int i, err;
 158
 159        memset(data, 0,
 160               sizeof(u64) * ETHSW_NUM_COUNTERS);
 161
 162        for (i = 0; i < ETHSW_NUM_COUNTERS; i++) {
 163                err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
 164                                          port_priv->ethsw_data->dpsw_handle,
 165                                          port_priv->idx,
 166                                          ethsw_ethtool_counters[i].id,
 167                                          &data[i]);
 168                if (err)
 169                        netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
 170                                   ethsw_ethtool_counters[i].name, err);
 171        }
 172}
 173
 174const struct ethtool_ops ethsw_port_ethtool_ops = {
 175        .get_drvinfo            = ethsw_get_drvinfo,
 176        .get_link               = ethtool_op_get_link,
 177        .get_link_ksettings     = ethsw_get_link_ksettings,
 178        .set_link_ksettings     = ethsw_set_link_ksettings,
 179        .get_strings            = ethsw_ethtool_get_strings,
 180        .get_ethtool_stats      = ethsw_ethtool_get_stats,
 181        .get_sset_count         = ethsw_ethtool_get_sset_count,
 182};
 183