linux/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-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 <linux/ethtool.h>
  11
  12#include "dpaa2-switch.h"
  13
  14static struct {
  15        enum dpsw_counter id;
  16        char name[ETH_GSTRING_LEN];
  17} dpaa2_switch_ethtool_counters[] =  {
  18        {DPSW_CNT_ING_FRAME,            "rx frames"},
  19        {DPSW_CNT_ING_BYTE,             "rx bytes"},
  20        {DPSW_CNT_ING_FLTR_FRAME,       "rx filtered frames"},
  21        {DPSW_CNT_ING_FRAME_DISCARD,    "rx discarded frames"},
  22        {DPSW_CNT_ING_BCAST_FRAME,      "rx b-cast frames"},
  23        {DPSW_CNT_ING_BCAST_BYTES,      "rx b-cast bytes"},
  24        {DPSW_CNT_ING_MCAST_FRAME,      "rx m-cast frames"},
  25        {DPSW_CNT_ING_MCAST_BYTE,       "rx m-cast bytes"},
  26        {DPSW_CNT_EGR_FRAME,            "tx frames"},
  27        {DPSW_CNT_EGR_BYTE,             "tx bytes"},
  28        {DPSW_CNT_EGR_FRAME_DISCARD,    "tx discarded frames"},
  29        {DPSW_CNT_ING_NO_BUFF_DISCARD,  "rx discarded no buffer frames"},
  30};
  31
  32#define DPAA2_SWITCH_NUM_COUNTERS       ARRAY_SIZE(dpaa2_switch_ethtool_counters)
  33
  34static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
  35                                     struct ethtool_drvinfo *drvinfo)
  36{
  37        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
  38        u16 version_major, version_minor;
  39        int err;
  40
  41        strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
  42
  43        err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
  44                                   &version_major,
  45                                   &version_minor);
  46        if (err)
  47                strscpy(drvinfo->fw_version, "N/A",
  48                        sizeof(drvinfo->fw_version));
  49        else
  50                snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
  51                         "%u.%u", version_major, version_minor);
  52
  53        strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
  54                sizeof(drvinfo->bus_info));
  55}
  56
  57static int
  58dpaa2_switch_get_link_ksettings(struct net_device *netdev,
  59                                struct ethtool_link_ksettings *link_ksettings)
  60{
  61        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
  62        struct dpsw_link_state state = {0};
  63        int err = 0;
  64
  65        err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
  66                                     port_priv->ethsw_data->dpsw_handle,
  67                                     port_priv->idx,
  68                                     &state);
  69        if (err) {
  70                netdev_err(netdev, "ERROR %d getting link state\n", err);
  71                goto out;
  72        }
  73
  74        /* At the moment, we have no way of interrogating the DPMAC
  75         * from the DPSW side or there may not exist a DPMAC at all.
  76         * Report only autoneg state, duplexity and speed.
  77         */
  78        if (state.options & DPSW_LINK_OPT_AUTONEG)
  79                link_ksettings->base.autoneg = AUTONEG_ENABLE;
  80        if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
  81                link_ksettings->base.duplex = DUPLEX_FULL;
  82        link_ksettings->base.speed = state.rate;
  83
  84out:
  85        return err;
  86}
  87
  88static int
  89dpaa2_switch_set_link_ksettings(struct net_device *netdev,
  90                                const struct ethtool_link_ksettings *link_ksettings)
  91{
  92        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
  93        struct ethsw_core *ethsw = port_priv->ethsw_data;
  94        struct dpsw_link_cfg cfg = {0};
  95        bool if_running;
  96        int err = 0, ret;
  97
  98        /* Interface needs to be down to change link settings */
  99        if_running = netif_running(netdev);
 100        if (if_running) {
 101                err = dpsw_if_disable(ethsw->mc_io, 0,
 102                                      ethsw->dpsw_handle,
 103                                      port_priv->idx);
 104                if (err) {
 105                        netdev_err(netdev, "dpsw_if_disable err %d\n", err);
 106                        return err;
 107                }
 108        }
 109
 110        cfg.rate = link_ksettings->base.speed;
 111        if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
 112                cfg.options |= DPSW_LINK_OPT_AUTONEG;
 113        else
 114                cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
 115        if (link_ksettings->base.duplex  == DUPLEX_HALF)
 116                cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
 117        else
 118                cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
 119
 120        err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
 121                                   port_priv->ethsw_data->dpsw_handle,
 122                                   port_priv->idx,
 123                                   &cfg);
 124
 125        if (if_running) {
 126                ret = dpsw_if_enable(ethsw->mc_io, 0,
 127                                     ethsw->dpsw_handle,
 128                                     port_priv->idx);
 129                if (ret) {
 130                        netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
 131                        return ret;
 132                }
 133        }
 134        return err;
 135}
 136
 137static int dpaa2_switch_ethtool_get_sset_count(struct net_device *dev, int sset)
 138{
 139        switch (sset) {
 140        case ETH_SS_STATS:
 141                return DPAA2_SWITCH_NUM_COUNTERS;
 142        default:
 143                return -EOPNOTSUPP;
 144        }
 145}
 146
 147static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
 148                                             u32 stringset, u8 *data)
 149{
 150        int i;
 151
 152        switch (stringset) {
 153        case ETH_SS_STATS:
 154                for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++)
 155                        memcpy(data + i * ETH_GSTRING_LEN,
 156                               dpaa2_switch_ethtool_counters[i].name,
 157                               ETH_GSTRING_LEN);
 158                break;
 159        }
 160}
 161
 162static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
 163                                           struct ethtool_stats *stats,
 164                                           u64 *data)
 165{
 166        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 167        int i, err;
 168
 169        for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
 170                err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
 171                                          port_priv->ethsw_data->dpsw_handle,
 172                                          port_priv->idx,
 173                                          dpaa2_switch_ethtool_counters[i].id,
 174                                          &data[i]);
 175                if (err)
 176                        netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
 177                                   dpaa2_switch_ethtool_counters[i].name, err);
 178        }
 179}
 180
 181const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
 182        .get_drvinfo            = dpaa2_switch_get_drvinfo,
 183        .get_link               = ethtool_op_get_link,
 184        .get_link_ksettings     = dpaa2_switch_get_link_ksettings,
 185        .set_link_ksettings     = dpaa2_switch_set_link_ksettings,
 186        .get_strings            = dpaa2_switch_ethtool_get_strings,
 187        .get_ethtool_stats      = dpaa2_switch_ethtool_get_stats,
 188        .get_sset_count         = dpaa2_switch_ethtool_get_sset_count,
 189};
 190