162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DPAA2 Ethernet Switch ethtool support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014-2016 Freescale Semiconductor Inc. 662306a36Sopenharmony_ci * Copyright 2017-2018 NXP 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/ethtool.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "dpaa2-switch.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic struct { 1562306a36Sopenharmony_ci enum dpsw_counter id; 1662306a36Sopenharmony_ci char name[ETH_GSTRING_LEN]; 1762306a36Sopenharmony_ci} dpaa2_switch_ethtool_counters[] = { 1862306a36Sopenharmony_ci {DPSW_CNT_ING_FRAME, "[hw] rx frames"}, 1962306a36Sopenharmony_ci {DPSW_CNT_ING_BYTE, "[hw] rx bytes"}, 2062306a36Sopenharmony_ci {DPSW_CNT_ING_FLTR_FRAME, "[hw] rx filtered frames"}, 2162306a36Sopenharmony_ci {DPSW_CNT_ING_FRAME_DISCARD, "[hw] rx discarded frames"}, 2262306a36Sopenharmony_ci {DPSW_CNT_ING_BCAST_FRAME, "[hw] rx bcast frames"}, 2362306a36Sopenharmony_ci {DPSW_CNT_ING_BCAST_BYTES, "[hw] rx bcast bytes"}, 2462306a36Sopenharmony_ci {DPSW_CNT_ING_MCAST_FRAME, "[hw] rx mcast frames"}, 2562306a36Sopenharmony_ci {DPSW_CNT_ING_MCAST_BYTE, "[hw] rx mcast bytes"}, 2662306a36Sopenharmony_ci {DPSW_CNT_EGR_FRAME, "[hw] tx frames"}, 2762306a36Sopenharmony_ci {DPSW_CNT_EGR_BYTE, "[hw] tx bytes"}, 2862306a36Sopenharmony_ci {DPSW_CNT_EGR_FRAME_DISCARD, "[hw] tx discarded frames"}, 2962306a36Sopenharmony_ci {DPSW_CNT_ING_NO_BUFF_DISCARD, "[hw] rx nobuffer discards"}, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DPAA2_SWITCH_NUM_COUNTERS ARRAY_SIZE(dpaa2_switch_ethtool_counters) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void dpaa2_switch_get_drvinfo(struct net_device *netdev, 3562306a36Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 3862306a36Sopenharmony_ci u16 version_major, version_minor; 3962306a36Sopenharmony_ci int err; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0, 4462306a36Sopenharmony_ci &version_major, 4562306a36Sopenharmony_ci &version_minor); 4662306a36Sopenharmony_ci if (err) 4762306a36Sopenharmony_ci strscpy(drvinfo->fw_version, "N/A", 4862306a36Sopenharmony_ci sizeof(drvinfo->fw_version)); 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), 5162306a36Sopenharmony_ci "%u.%u", version_major, version_minor); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent), 5462306a36Sopenharmony_ci sizeof(drvinfo->bus_info)); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int 5862306a36Sopenharmony_cidpaa2_switch_get_link_ksettings(struct net_device *netdev, 5962306a36Sopenharmony_ci struct ethtool_link_ksettings *link_ksettings) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 6262306a36Sopenharmony_ci struct dpsw_link_state state = {0}; 6362306a36Sopenharmony_ci int err; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (dpaa2_switch_port_is_type_phy(port_priv)) { 6862306a36Sopenharmony_ci err = phylink_ethtool_ksettings_get(port_priv->mac->phylink, 6962306a36Sopenharmony_ci link_ksettings); 7062306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 7162306a36Sopenharmony_ci return err; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, 7762306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 7862306a36Sopenharmony_ci port_priv->idx, 7962306a36Sopenharmony_ci &state); 8062306a36Sopenharmony_ci if (err) { 8162306a36Sopenharmony_ci netdev_err(netdev, "ERROR %d getting link state\n", err); 8262306a36Sopenharmony_ci goto out; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* At the moment, we have no way of interrogating the DPMAC 8662306a36Sopenharmony_ci * from the DPSW side or there may not exist a DPMAC at all. 8762306a36Sopenharmony_ci * Report only autoneg state, duplexity and speed. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (state.options & DPSW_LINK_OPT_AUTONEG) 9062306a36Sopenharmony_ci link_ksettings->base.autoneg = AUTONEG_ENABLE; 9162306a36Sopenharmony_ci if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX)) 9262306a36Sopenharmony_ci link_ksettings->base.duplex = DUPLEX_FULL; 9362306a36Sopenharmony_ci link_ksettings->base.speed = state.rate; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciout: 9662306a36Sopenharmony_ci return err; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int 10062306a36Sopenharmony_cidpaa2_switch_set_link_ksettings(struct net_device *netdev, 10162306a36Sopenharmony_ci const struct ethtool_link_ksettings *link_ksettings) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 10462306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 10562306a36Sopenharmony_ci struct dpsw_link_cfg cfg = {0}; 10662306a36Sopenharmony_ci bool if_running; 10762306a36Sopenharmony_ci int err = 0, ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (dpaa2_switch_port_is_type_phy(port_priv)) { 11262306a36Sopenharmony_ci err = phylink_ethtool_ksettings_set(port_priv->mac->phylink, 11362306a36Sopenharmony_ci link_ksettings); 11462306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 11562306a36Sopenharmony_ci return err; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Interface needs to be down to change link settings */ 12162306a36Sopenharmony_ci if_running = netif_running(netdev); 12262306a36Sopenharmony_ci if (if_running) { 12362306a36Sopenharmony_ci err = dpsw_if_disable(ethsw->mc_io, 0, 12462306a36Sopenharmony_ci ethsw->dpsw_handle, 12562306a36Sopenharmony_ci port_priv->idx); 12662306a36Sopenharmony_ci if (err) { 12762306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_disable err %d\n", err); 12862306a36Sopenharmony_ci return err; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci cfg.rate = link_ksettings->base.speed; 13362306a36Sopenharmony_ci if (link_ksettings->base.autoneg == AUTONEG_ENABLE) 13462306a36Sopenharmony_ci cfg.options |= DPSW_LINK_OPT_AUTONEG; 13562306a36Sopenharmony_ci else 13662306a36Sopenharmony_ci cfg.options &= ~DPSW_LINK_OPT_AUTONEG; 13762306a36Sopenharmony_ci if (link_ksettings->base.duplex == DUPLEX_HALF) 13862306a36Sopenharmony_ci cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX; 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0, 14362306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 14462306a36Sopenharmony_ci port_priv->idx, 14562306a36Sopenharmony_ci &cfg); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (if_running) { 14862306a36Sopenharmony_ci ret = dpsw_if_enable(ethsw->mc_io, 0, 14962306a36Sopenharmony_ci ethsw->dpsw_handle, 15062306a36Sopenharmony_ci port_priv->idx); 15162306a36Sopenharmony_ci if (ret) { 15262306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_enable err %d\n", ret); 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci return err; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int 16062306a36Sopenharmony_cidpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci switch (sset) { 16362306a36Sopenharmony_ci case ETH_SS_STATS: 16462306a36Sopenharmony_ci return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count(); 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci return -EOPNOTSUPP; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void dpaa2_switch_ethtool_get_strings(struct net_device *netdev, 17162306a36Sopenharmony_ci u32 stringset, u8 *data) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u8 *p = data; 17462306a36Sopenharmony_ci int i; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci switch (stringset) { 17762306a36Sopenharmony_ci case ETH_SS_STATS: 17862306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) { 17962306a36Sopenharmony_ci memcpy(p, dpaa2_switch_ethtool_counters[i].name, 18062306a36Sopenharmony_ci ETH_GSTRING_LEN); 18162306a36Sopenharmony_ci p += ETH_GSTRING_LEN; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci dpaa2_mac_get_strings(p); 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void dpaa2_switch_ethtool_get_stats(struct net_device *netdev, 18962306a36Sopenharmony_ci struct ethtool_stats *stats, 19062306a36Sopenharmony_ci u64 *data) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 19362306a36Sopenharmony_ci int i, err; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) { 19662306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 19762306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 19862306a36Sopenharmony_ci port_priv->idx, 19962306a36Sopenharmony_ci dpaa2_switch_ethtool_counters[i].id, 20062306a36Sopenharmony_ci &data[i]); 20162306a36Sopenharmony_ci if (err) 20262306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n", 20362306a36Sopenharmony_ci dpaa2_switch_ethtool_counters[i].name, err); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (dpaa2_switch_port_has_mac(port_priv)) 20962306a36Sopenharmony_ci dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciconst struct ethtool_ops dpaa2_switch_port_ethtool_ops = { 21562306a36Sopenharmony_ci .get_drvinfo = dpaa2_switch_get_drvinfo, 21662306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 21762306a36Sopenharmony_ci .get_link_ksettings = dpaa2_switch_get_link_ksettings, 21862306a36Sopenharmony_ci .set_link_ksettings = dpaa2_switch_set_link_ksettings, 21962306a36Sopenharmony_ci .get_strings = dpaa2_switch_ethtool_get_strings, 22062306a36Sopenharmony_ci .get_ethtool_stats = dpaa2_switch_ethtool_get_stats, 22162306a36Sopenharmony_ci .get_sset_count = dpaa2_switch_ethtool_get_sset_count, 22262306a36Sopenharmony_ci}; 223