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 14 static struct { 15 enum dpsw_counter id; 16 char name[ETH_GSTRING_LEN]; 17 } dpaa2_switch_ethtool_counters[] = { 18 {DPSW_CNT_ING_FRAME, "[hw] rx frames"}, 19 {DPSW_CNT_ING_BYTE, "[hw] rx bytes"}, 20 {DPSW_CNT_ING_FLTR_FRAME, "[hw] rx filtered frames"}, 21 {DPSW_CNT_ING_FRAME_DISCARD, "[hw] rx discarded frames"}, 22 {DPSW_CNT_ING_BCAST_FRAME, "[hw] rx bcast frames"}, 23 {DPSW_CNT_ING_BCAST_BYTES, "[hw] rx bcast bytes"}, 24 {DPSW_CNT_ING_MCAST_FRAME, "[hw] rx mcast frames"}, 25 {DPSW_CNT_ING_MCAST_BYTE, "[hw] rx mcast bytes"}, 26 {DPSW_CNT_EGR_FRAME, "[hw] tx frames"}, 27 {DPSW_CNT_EGR_BYTE, "[hw] tx bytes"}, 28 {DPSW_CNT_EGR_FRAME_DISCARD, "[hw] tx discarded frames"}, 29 {DPSW_CNT_ING_NO_BUFF_DISCARD, "[hw] rx nobuffer discards"}, 30 }; 31 32 #define DPAA2_SWITCH_NUM_COUNTERS ARRAY_SIZE(dpaa2_switch_ethtool_counters) 33 34 static 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 57 static int 58 dpaa2_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; 64 65 mutex_lock(&port_priv->mac_lock); 66 67 if (dpaa2_switch_port_is_type_phy(port_priv)) { 68 err = phylink_ethtool_ksettings_get(port_priv->mac->phylink, 69 link_ksettings); 70 mutex_unlock(&port_priv->mac_lock); 71 return err; 72 } 73 74 mutex_unlock(&port_priv->mac_lock); 75 76 err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, 77 port_priv->ethsw_data->dpsw_handle, 78 port_priv->idx, 79 &state); 80 if (err) { 81 netdev_err(netdev, "ERROR %d getting link state\n", err); 82 goto out; 83 } 84 85 /* At the moment, we have no way of interrogating the DPMAC 86 * from the DPSW side or there may not exist a DPMAC at all. 87 * Report only autoneg state, duplexity and speed. 88 */ 89 if (state.options & DPSW_LINK_OPT_AUTONEG) 90 link_ksettings->base.autoneg = AUTONEG_ENABLE; 91 if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX)) 92 link_ksettings->base.duplex = DUPLEX_FULL; 93 link_ksettings->base.speed = state.rate; 94 95 out: 96 return err; 97 } 98 99 static int 100 dpaa2_switch_set_link_ksettings(struct net_device *netdev, 101 const struct ethtool_link_ksettings *link_ksettings) 102 { 103 struct ethsw_port_priv *port_priv = netdev_priv(netdev); 104 struct ethsw_core *ethsw = port_priv->ethsw_data; 105 struct dpsw_link_cfg cfg = {0}; 106 bool if_running; 107 int err = 0, ret; 108 109 mutex_lock(&port_priv->mac_lock); 110 111 if (dpaa2_switch_port_is_type_phy(port_priv)) { 112 err = phylink_ethtool_ksettings_set(port_priv->mac->phylink, 113 link_ksettings); 114 mutex_unlock(&port_priv->mac_lock); 115 return err; 116 } 117 118 mutex_unlock(&port_priv->mac_lock); 119 120 /* Interface needs to be down to change link settings */ 121 if_running = netif_running(netdev); 122 if (if_running) { 123 err = dpsw_if_disable(ethsw->mc_io, 0, 124 ethsw->dpsw_handle, 125 port_priv->idx); 126 if (err) { 127 netdev_err(netdev, "dpsw_if_disable err %d\n", err); 128 return err; 129 } 130 } 131 132 cfg.rate = link_ksettings->base.speed; 133 if (link_ksettings->base.autoneg == AUTONEG_ENABLE) 134 cfg.options |= DPSW_LINK_OPT_AUTONEG; 135 else 136 cfg.options &= ~DPSW_LINK_OPT_AUTONEG; 137 if (link_ksettings->base.duplex == DUPLEX_HALF) 138 cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX; 139 else 140 cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX; 141 142 err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0, 143 port_priv->ethsw_data->dpsw_handle, 144 port_priv->idx, 145 &cfg); 146 147 if (if_running) { 148 ret = dpsw_if_enable(ethsw->mc_io, 0, 149 ethsw->dpsw_handle, 150 port_priv->idx); 151 if (ret) { 152 netdev_err(netdev, "dpsw_if_enable err %d\n", ret); 153 return ret; 154 } 155 } 156 return err; 157 } 158 159 static int 160 dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset) 161 { 162 switch (sset) { 163 case ETH_SS_STATS: 164 return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count(); 165 default: 166 return -EOPNOTSUPP; 167 } 168 } 169 170 static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev, 171 u32 stringset, u8 *data) 172 { 173 u8 *p = data; 174 int i; 175 176 switch (stringset) { 177 case ETH_SS_STATS: 178 for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) { 179 memcpy(p, dpaa2_switch_ethtool_counters[i].name, 180 ETH_GSTRING_LEN); 181 p += ETH_GSTRING_LEN; 182 } 183 dpaa2_mac_get_strings(p); 184 break; 185 } 186 } 187 188 static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev, 189 struct ethtool_stats *stats, 190 u64 *data) 191 { 192 struct ethsw_port_priv *port_priv = netdev_priv(netdev); 193 int i, err; 194 195 for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) { 196 err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 197 port_priv->ethsw_data->dpsw_handle, 198 port_priv->idx, 199 dpaa2_switch_ethtool_counters[i].id, 200 &data[i]); 201 if (err) 202 netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n", 203 dpaa2_switch_ethtool_counters[i].name, err); 204 } 205 206 mutex_lock(&port_priv->mac_lock); 207 208 if (dpaa2_switch_port_has_mac(port_priv)) 209 dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i); 210 211 mutex_unlock(&port_priv->mac_lock); 212 } 213 214 const struct ethtool_ops dpaa2_switch_port_ethtool_ops = { 215 .get_drvinfo = dpaa2_switch_get_drvinfo, 216 .get_link = ethtool_op_get_link, 217 .get_link_ksettings = dpaa2_switch_get_link_ksettings, 218 .set_link_ksettings = dpaa2_switch_set_link_ksettings, 219 .get_strings = dpaa2_switch_ethtool_get_strings, 220 .get_ethtool_stats = dpaa2_switch_ethtool_get_stats, 221 .get_sset_count = dpaa2_switch_ethtool_get_sset_count, 222 }; 223