1 /* 2 * FUJITSU Extended Socket Network Device driver 3 * Copyright (c) 2015 FUJITSU LIMITED 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms and conditions of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program; if not, see <http://www.gnu.org/licenses/>. 16 * 17 * The full GNU General Public License is included in this distribution in 18 * the file called "COPYING". 19 * 20 */ 21 22 /* ethtool support for fjes */ 23 24 #include <linux/vmalloc.h> 25 #include <linux/netdevice.h> 26 #include <linux/ethtool.h> 27 #include <linux/platform_device.h> 28 29 #include "fjes.h" 30 31 struct fjes_stats { 32 char stat_string[ETH_GSTRING_LEN]; 33 int sizeof_stat; 34 int stat_offset; 35 }; 36 37 #define FJES_STAT(name, stat) { \ 38 .stat_string = name, \ 39 .sizeof_stat = FIELD_SIZEOF(struct fjes_adapter, stat), \ 40 .stat_offset = offsetof(struct fjes_adapter, stat) \ 41 } 42 43 static const struct fjes_stats fjes_gstrings_stats[] = { 44 FJES_STAT("rx_packets", stats64.rx_packets), 45 FJES_STAT("tx_packets", stats64.tx_packets), 46 FJES_STAT("rx_bytes", stats64.rx_bytes), 47 FJES_STAT("tx_bytes", stats64.rx_bytes), 48 FJES_STAT("rx_dropped", stats64.rx_dropped), 49 FJES_STAT("tx_dropped", stats64.tx_dropped), 50 }; 51 52 #define FJES_EP_STATS_LEN 14 53 #define FJES_STATS_LEN \ 54 (ARRAY_SIZE(fjes_gstrings_stats) + \ 55 ((&((struct fjes_adapter *)netdev_priv(netdev))->hw)->max_epid - 1) * \ 56 FJES_EP_STATS_LEN) 57 58 static void fjes_get_ethtool_stats(struct net_device *netdev, 59 struct ethtool_stats *stats, u64 *data) 60 { 61 struct fjes_adapter *adapter = netdev_priv(netdev); 62 struct fjes_hw *hw = &adapter->hw; 63 int epidx; 64 char *p; 65 int i; 66 67 for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { 68 p = (char *)adapter + fjes_gstrings_stats[i].stat_offset; 69 data[i] = (fjes_gstrings_stats[i].sizeof_stat == sizeof(u64)) 70 ? *(u64 *)p : *(u32 *)p; 71 } 72 for (epidx = 0; epidx < hw->max_epid; epidx++) { 73 if (epidx == hw->my_epid) 74 continue; 75 data[i++] = hw->ep_shm_info[epidx].ep_stats 76 .com_regist_buf_exec; 77 data[i++] = hw->ep_shm_info[epidx].ep_stats 78 .com_unregist_buf_exec; 79 data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_rx; 80 data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_unshare; 81 data[i++] = hw->ep_shm_info[epidx].ep_stats 82 .send_intr_zoneupdate; 83 data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_rx; 84 data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_unshare; 85 data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_stop; 86 data[i++] = hw->ep_shm_info[epidx].ep_stats 87 .recv_intr_zoneupdate; 88 data[i++] = hw->ep_shm_info[epidx].ep_stats.tx_buffer_full; 89 data[i++] = hw->ep_shm_info[epidx].ep_stats 90 .tx_dropped_not_shared; 91 data[i++] = hw->ep_shm_info[epidx].ep_stats 92 .tx_dropped_ver_mismatch; 93 data[i++] = hw->ep_shm_info[epidx].ep_stats 94 .tx_dropped_buf_size_mismatch; 95 data[i++] = hw->ep_shm_info[epidx].ep_stats 96 .tx_dropped_vlanid_mismatch; 97 } 98 } 99 100 static void fjes_get_strings(struct net_device *netdev, 101 u32 stringset, u8 *data) 102 { 103 struct fjes_adapter *adapter = netdev_priv(netdev); 104 struct fjes_hw *hw = &adapter->hw; 105 u8 *p = data; 106 int i; 107 108 switch (stringset) { 109 case ETH_SS_STATS: 110 for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { 111 memcpy(p, fjes_gstrings_stats[i].stat_string, 112 ETH_GSTRING_LEN); 113 p += ETH_GSTRING_LEN; 114 } 115 for (i = 0; i < hw->max_epid; i++) { 116 if (i == hw->my_epid) 117 continue; 118 sprintf(p, "ep%u_com_regist_buf_exec", i); 119 p += ETH_GSTRING_LEN; 120 sprintf(p, "ep%u_com_unregist_buf_exec", i); 121 p += ETH_GSTRING_LEN; 122 sprintf(p, "ep%u_send_intr_rx", i); 123 p += ETH_GSTRING_LEN; 124 sprintf(p, "ep%u_send_intr_unshare", i); 125 p += ETH_GSTRING_LEN; 126 sprintf(p, "ep%u_send_intr_zoneupdate", i); 127 p += ETH_GSTRING_LEN; 128 sprintf(p, "ep%u_recv_intr_rx", i); 129 p += ETH_GSTRING_LEN; 130 sprintf(p, "ep%u_recv_intr_unshare", i); 131 p += ETH_GSTRING_LEN; 132 sprintf(p, "ep%u_recv_intr_stop", i); 133 p += ETH_GSTRING_LEN; 134 sprintf(p, "ep%u_recv_intr_zoneupdate", i); 135 p += ETH_GSTRING_LEN; 136 sprintf(p, "ep%u_tx_buffer_full", i); 137 p += ETH_GSTRING_LEN; 138 sprintf(p, "ep%u_tx_dropped_not_shared", i); 139 p += ETH_GSTRING_LEN; 140 sprintf(p, "ep%u_tx_dropped_ver_mismatch", i); 141 p += ETH_GSTRING_LEN; 142 sprintf(p, "ep%u_tx_dropped_buf_size_mismatch", i); 143 p += ETH_GSTRING_LEN; 144 sprintf(p, "ep%u_tx_dropped_vlanid_mismatch", i); 145 p += ETH_GSTRING_LEN; 146 } 147 break; 148 } 149 } 150 151 static int fjes_get_sset_count(struct net_device *netdev, int sset) 152 { 153 switch (sset) { 154 case ETH_SS_STATS: 155 return FJES_STATS_LEN; 156 default: 157 return -EOPNOTSUPP; 158 } 159 } 160 161 static void fjes_get_drvinfo(struct net_device *netdev, 162 struct ethtool_drvinfo *drvinfo) 163 { 164 struct fjes_adapter *adapter = netdev_priv(netdev); 165 struct platform_device *plat_dev; 166 167 plat_dev = adapter->plat_dev; 168 169 strlcpy(drvinfo->driver, fjes_driver_name, sizeof(drvinfo->driver)); 170 strlcpy(drvinfo->version, fjes_driver_version, 171 sizeof(drvinfo->version)); 172 173 strlcpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version)); 174 snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info), 175 "platform:%s", plat_dev->name); 176 } 177 178 static int fjes_get_link_ksettings(struct net_device *netdev, 179 struct ethtool_link_ksettings *ecmd) 180 { 181 ethtool_link_ksettings_zero_link_mode(ecmd, supported); 182 ethtool_link_ksettings_zero_link_mode(ecmd, advertising); 183 ecmd->base.duplex = DUPLEX_FULL; 184 ecmd->base.autoneg = AUTONEG_DISABLE; 185 ecmd->base.port = PORT_NONE; 186 ecmd->base.speed = 20000; /* 20Gb/s */ 187 188 return 0; 189 } 190 191 static int fjes_get_regs_len(struct net_device *netdev) 192 { 193 #define FJES_REGS_LEN 37 194 return FJES_REGS_LEN * sizeof(u32); 195 } 196 197 static void fjes_get_regs(struct net_device *netdev, 198 struct ethtool_regs *regs, void *p) 199 { 200 struct fjes_adapter *adapter = netdev_priv(netdev); 201 struct fjes_hw *hw = &adapter->hw; 202 u32 *regs_buff = p; 203 204 memset(p, 0, FJES_REGS_LEN * sizeof(u32)); 205 206 regs->version = 1; 207 208 /* Information registers */ 209 regs_buff[0] = rd32(XSCT_OWNER_EPID); 210 regs_buff[1] = rd32(XSCT_MAX_EP); 211 212 /* Device Control registers */ 213 regs_buff[4] = rd32(XSCT_DCTL); 214 215 /* Command Control registers */ 216 regs_buff[8] = rd32(XSCT_CR); 217 regs_buff[9] = rd32(XSCT_CS); 218 regs_buff[10] = rd32(XSCT_SHSTSAL); 219 regs_buff[11] = rd32(XSCT_SHSTSAH); 220 221 regs_buff[13] = rd32(XSCT_REQBL); 222 regs_buff[14] = rd32(XSCT_REQBAL); 223 regs_buff[15] = rd32(XSCT_REQBAH); 224 225 regs_buff[17] = rd32(XSCT_RESPBL); 226 regs_buff[18] = rd32(XSCT_RESPBAL); 227 regs_buff[19] = rd32(XSCT_RESPBAH); 228 229 /* Interrupt Control registers */ 230 regs_buff[32] = rd32(XSCT_IS); 231 regs_buff[33] = rd32(XSCT_IMS); 232 regs_buff[34] = rd32(XSCT_IMC); 233 regs_buff[35] = rd32(XSCT_IG); 234 regs_buff[36] = rd32(XSCT_ICTL); 235 } 236 237 static int fjes_set_dump(struct net_device *netdev, struct ethtool_dump *dump) 238 { 239 struct fjes_adapter *adapter = netdev_priv(netdev); 240 struct fjes_hw *hw = &adapter->hw; 241 int ret = 0; 242 243 if (dump->flag) { 244 if (hw->debug_mode) 245 return -EPERM; 246 247 hw->debug_mode = dump->flag; 248 249 /* enable debug mode */ 250 mutex_lock(&hw->hw_info.lock); 251 ret = fjes_hw_start_debug(hw); 252 mutex_unlock(&hw->hw_info.lock); 253 254 if (ret) 255 hw->debug_mode = 0; 256 } else { 257 if (!hw->debug_mode) 258 return -EPERM; 259 260 /* disable debug mode */ 261 mutex_lock(&hw->hw_info.lock); 262 ret = fjes_hw_stop_debug(hw); 263 mutex_unlock(&hw->hw_info.lock); 264 } 265 266 return ret; 267 } 268 269 static int fjes_get_dump_flag(struct net_device *netdev, 270 struct ethtool_dump *dump) 271 { 272 struct fjes_adapter *adapter = netdev_priv(netdev); 273 struct fjes_hw *hw = &adapter->hw; 274 275 dump->len = hw->hw_info.trace_size; 276 dump->version = 1; 277 dump->flag = hw->debug_mode; 278 279 return 0; 280 } 281 282 static int fjes_get_dump_data(struct net_device *netdev, 283 struct ethtool_dump *dump, void *buf) 284 { 285 struct fjes_adapter *adapter = netdev_priv(netdev); 286 struct fjes_hw *hw = &adapter->hw; 287 int ret = 0; 288 289 if (hw->hw_info.trace) 290 memcpy(buf, hw->hw_info.trace, hw->hw_info.trace_size); 291 else 292 ret = -EPERM; 293 294 return ret; 295 } 296 297 static const struct ethtool_ops fjes_ethtool_ops = { 298 .get_drvinfo = fjes_get_drvinfo, 299 .get_ethtool_stats = fjes_get_ethtool_stats, 300 .get_strings = fjes_get_strings, 301 .get_sset_count = fjes_get_sset_count, 302 .get_regs = fjes_get_regs, 303 .get_regs_len = fjes_get_regs_len, 304 .set_dump = fjes_set_dump, 305 .get_dump_flag = fjes_get_dump_flag, 306 .get_dump_data = fjes_get_dump_data, 307 .get_link_ksettings = fjes_get_link_ksettings, 308 }; 309 310 void fjes_set_ethtool_ops(struct net_device *netdev) 311 { 312 netdev->ethtool_ops = &fjes_ethtool_ops; 313 } 314