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