Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * DPAA2 Ethernet Switch ethtool support
0004  *
0005  * Copyright 2014-2016 Freescale Semiconductor Inc.
0006  * Copyright 2017-2018 NXP
0007  *
0008  */
0009 
0010 #include <linux/ethtool.h>
0011 
0012 #include "dpaa2-switch.h"
0013 
0014 static struct {
0015     enum dpsw_counter id;
0016     char name[ETH_GSTRING_LEN];
0017 } dpaa2_switch_ethtool_counters[] =  {
0018     {DPSW_CNT_ING_FRAME,        "[hw] rx frames"},
0019     {DPSW_CNT_ING_BYTE,     "[hw] rx bytes"},
0020     {DPSW_CNT_ING_FLTR_FRAME,   "[hw] rx filtered frames"},
0021     {DPSW_CNT_ING_FRAME_DISCARD,    "[hw] rx discarded frames"},
0022     {DPSW_CNT_ING_BCAST_FRAME,  "[hw] rx bcast frames"},
0023     {DPSW_CNT_ING_BCAST_BYTES,  "[hw] rx bcast bytes"},
0024     {DPSW_CNT_ING_MCAST_FRAME,  "[hw] rx mcast frames"},
0025     {DPSW_CNT_ING_MCAST_BYTE,   "[hw] rx mcast bytes"},
0026     {DPSW_CNT_EGR_FRAME,        "[hw] tx frames"},
0027     {DPSW_CNT_EGR_BYTE,     "[hw] tx bytes"},
0028     {DPSW_CNT_EGR_FRAME_DISCARD,    "[hw] tx discarded frames"},
0029     {DPSW_CNT_ING_NO_BUFF_DISCARD,  "[hw] rx nobuffer discards"},
0030 };
0031 
0032 #define DPAA2_SWITCH_NUM_COUNTERS   ARRAY_SIZE(dpaa2_switch_ethtool_counters)
0033 
0034 static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
0035                      struct ethtool_drvinfo *drvinfo)
0036 {
0037     struct ethsw_port_priv *port_priv = netdev_priv(netdev);
0038     u16 version_major, version_minor;
0039     int err;
0040 
0041     strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
0042 
0043     err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
0044                    &version_major,
0045                    &version_minor);
0046     if (err)
0047         strscpy(drvinfo->fw_version, "N/A",
0048             sizeof(drvinfo->fw_version));
0049     else
0050         snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
0051              "%u.%u", version_major, version_minor);
0052 
0053     strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
0054         sizeof(drvinfo->bus_info));
0055 }
0056 
0057 static int
0058 dpaa2_switch_get_link_ksettings(struct net_device *netdev,
0059                 struct ethtool_link_ksettings *link_ksettings)
0060 {
0061     struct ethsw_port_priv *port_priv = netdev_priv(netdev);
0062     struct dpsw_link_state state = {0};
0063     int err = 0;
0064 
0065     if (dpaa2_switch_port_is_type_phy(port_priv))
0066         return phylink_ethtool_ksettings_get(port_priv->mac->phylink,
0067                              link_ksettings);
0068 
0069     err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
0070                      port_priv->ethsw_data->dpsw_handle,
0071                      port_priv->idx,
0072                      &state);
0073     if (err) {
0074         netdev_err(netdev, "ERROR %d getting link state\n", err);
0075         goto out;
0076     }
0077 
0078     /* At the moment, we have no way of interrogating the DPMAC
0079      * from the DPSW side or there may not exist a DPMAC at all.
0080      * Report only autoneg state, duplexity and speed.
0081      */
0082     if (state.options & DPSW_LINK_OPT_AUTONEG)
0083         link_ksettings->base.autoneg = AUTONEG_ENABLE;
0084     if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
0085         link_ksettings->base.duplex = DUPLEX_FULL;
0086     link_ksettings->base.speed = state.rate;
0087 
0088 out:
0089     return err;
0090 }
0091 
0092 static int
0093 dpaa2_switch_set_link_ksettings(struct net_device *netdev,
0094                 const struct ethtool_link_ksettings *link_ksettings)
0095 {
0096     struct ethsw_port_priv *port_priv = netdev_priv(netdev);
0097     struct ethsw_core *ethsw = port_priv->ethsw_data;
0098     struct dpsw_link_cfg cfg = {0};
0099     bool if_running;
0100     int err = 0, ret;
0101 
0102     if (dpaa2_switch_port_is_type_phy(port_priv))
0103         return phylink_ethtool_ksettings_set(port_priv->mac->phylink,
0104                              link_ksettings);
0105 
0106     /* Interface needs to be down to change link settings */
0107     if_running = netif_running(netdev);
0108     if (if_running) {
0109         err = dpsw_if_disable(ethsw->mc_io, 0,
0110                       ethsw->dpsw_handle,
0111                       port_priv->idx);
0112         if (err) {
0113             netdev_err(netdev, "dpsw_if_disable err %d\n", err);
0114             return err;
0115         }
0116     }
0117 
0118     cfg.rate = link_ksettings->base.speed;
0119     if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
0120         cfg.options |= DPSW_LINK_OPT_AUTONEG;
0121     else
0122         cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
0123     if (link_ksettings->base.duplex  == DUPLEX_HALF)
0124         cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
0125     else
0126         cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
0127 
0128     err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
0129                    port_priv->ethsw_data->dpsw_handle,
0130                    port_priv->idx,
0131                    &cfg);
0132 
0133     if (if_running) {
0134         ret = dpsw_if_enable(ethsw->mc_io, 0,
0135                      ethsw->dpsw_handle,
0136                      port_priv->idx);
0137         if (ret) {
0138             netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
0139             return ret;
0140         }
0141     }
0142     return err;
0143 }
0144 
0145 static int
0146 dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset)
0147 {
0148     struct ethsw_port_priv *port_priv = netdev_priv(netdev);
0149     int num_ss_stats = DPAA2_SWITCH_NUM_COUNTERS;
0150 
0151     switch (sset) {
0152     case ETH_SS_STATS:
0153         if (port_priv->mac)
0154             num_ss_stats += dpaa2_mac_get_sset_count();
0155         return num_ss_stats;
0156     default:
0157         return -EOPNOTSUPP;
0158     }
0159 }
0160 
0161 static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
0162                          u32 stringset, u8 *data)
0163 {
0164     struct ethsw_port_priv *port_priv = netdev_priv(netdev);
0165     u8 *p = data;
0166     int i;
0167 
0168     switch (stringset) {
0169     case ETH_SS_STATS:
0170         for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
0171             memcpy(p, dpaa2_switch_ethtool_counters[i].name,
0172                    ETH_GSTRING_LEN);
0173             p += ETH_GSTRING_LEN;
0174         }
0175         if (port_priv->mac)
0176             dpaa2_mac_get_strings(p);
0177         break;
0178     }
0179 }
0180 
0181 static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
0182                        struct ethtool_stats *stats,
0183                        u64 *data)
0184 {
0185     struct ethsw_port_priv *port_priv = netdev_priv(netdev);
0186     int i, err;
0187 
0188     for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
0189         err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
0190                       port_priv->ethsw_data->dpsw_handle,
0191                       port_priv->idx,
0192                       dpaa2_switch_ethtool_counters[i].id,
0193                       &data[i]);
0194         if (err)
0195             netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
0196                    dpaa2_switch_ethtool_counters[i].name, err);
0197     }
0198 
0199     if (port_priv->mac)
0200         dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i);
0201 }
0202 
0203 const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
0204     .get_drvinfo        = dpaa2_switch_get_drvinfo,
0205     .get_link       = ethtool_op_get_link,
0206     .get_link_ksettings = dpaa2_switch_get_link_ksettings,
0207     .set_link_ksettings = dpaa2_switch_set_link_ksettings,
0208     .get_strings        = dpaa2_switch_ethtool_get_strings,
0209     .get_ethtool_stats  = dpaa2_switch_ethtool_get_stats,
0210     .get_sset_count     = dpaa2_switch_ethtool_get_sset_count,
0211 };