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_settings(struct net_device *netdev, 179 struct ethtool_cmd *ecmd) 180 { 181 ecmd->supported = 0; 182 ecmd->advertising = 0; 183 ecmd->duplex = DUPLEX_FULL; 184 ecmd->autoneg = AUTONEG_DISABLE; 185 ecmd->transceiver = XCVR_DUMMY1; 186 ecmd->port = PORT_NONE; 187 ethtool_cmd_speed_set(ecmd, 20000); /* 20Gb/s */ 188 189 return 0; 190 } 191 192 static int fjes_get_regs_len(struct net_device *netdev) 193 { 194 #define FJES_REGS_LEN 37 195 return FJES_REGS_LEN * sizeof(u32); 196 } 197 198 static void fjes_get_regs(struct net_device *netdev, 199 struct ethtool_regs *regs, void *p) 200 { 201 struct fjes_adapter *adapter = netdev_priv(netdev); 202 struct fjes_hw *hw = &adapter->hw; 203 u32 *regs_buff = p; 204 205 memset(p, 0, FJES_REGS_LEN * sizeof(u32)); 206 207 regs->version = 1; 208 209 /* Information registers */ 210 regs_buff[0] = rd32(XSCT_OWNER_EPID); 211 regs_buff[1] = rd32(XSCT_MAX_EP); 212 213 /* Device Control registers */ 214 regs_buff[4] = rd32(XSCT_DCTL); 215 216 /* Command Control registers */ 217 regs_buff[8] = rd32(XSCT_CR); 218 regs_buff[9] = rd32(XSCT_CS); 219 regs_buff[10] = rd32(XSCT_SHSTSAL); 220 regs_buff[11] = rd32(XSCT_SHSTSAH); 221 222 regs_buff[13] = rd32(XSCT_REQBL); 223 regs_buff[14] = rd32(XSCT_REQBAL); 224 regs_buff[15] = rd32(XSCT_REQBAH); 225 226 regs_buff[17] = rd32(XSCT_RESPBL); 227 regs_buff[18] = rd32(XSCT_RESPBAL); 228 regs_buff[19] = rd32(XSCT_RESPBAH); 229 230 /* Interrupt Control registers */ 231 regs_buff[32] = rd32(XSCT_IS); 232 regs_buff[33] = rd32(XSCT_IMS); 233 regs_buff[34] = rd32(XSCT_IMC); 234 regs_buff[35] = rd32(XSCT_IG); 235 regs_buff[36] = rd32(XSCT_ICTL); 236 } 237 238 static int fjes_set_dump(struct net_device *netdev, struct ethtool_dump *dump) 239 { 240 struct fjes_adapter *adapter = netdev_priv(netdev); 241 struct fjes_hw *hw = &adapter->hw; 242 int ret = 0; 243 244 if (dump->flag) { 245 if (hw->debug_mode) 246 return -EPERM; 247 248 hw->debug_mode = dump->flag; 249 250 /* enable debug mode */ 251 mutex_lock(&hw->hw_info.lock); 252 ret = fjes_hw_start_debug(hw); 253 mutex_unlock(&hw->hw_info.lock); 254 255 if (ret) 256 hw->debug_mode = 0; 257 } else { 258 if (!hw->debug_mode) 259 return -EPERM; 260 261 /* disable debug mode */ 262 mutex_lock(&hw->hw_info.lock); 263 ret = fjes_hw_stop_debug(hw); 264 mutex_unlock(&hw->hw_info.lock); 265 } 266 267 return ret; 268 } 269 270 static int fjes_get_dump_flag(struct net_device *netdev, 271 struct ethtool_dump *dump) 272 { 273 struct fjes_adapter *adapter = netdev_priv(netdev); 274 struct fjes_hw *hw = &adapter->hw; 275 276 dump->len = hw->hw_info.trace_size; 277 dump->version = 1; 278 dump->flag = hw->debug_mode; 279 280 return 0; 281 } 282 283 static int fjes_get_dump_data(struct net_device *netdev, 284 struct ethtool_dump *dump, void *buf) 285 { 286 struct fjes_adapter *adapter = netdev_priv(netdev); 287 struct fjes_hw *hw = &adapter->hw; 288 int ret = 0; 289 290 if (hw->hw_info.trace) 291 memcpy(buf, hw->hw_info.trace, hw->hw_info.trace_size); 292 else 293 ret = -EPERM; 294 295 return ret; 296 } 297 298 static const struct ethtool_ops fjes_ethtool_ops = { 299 .get_settings = fjes_get_settings, 300 .get_drvinfo = fjes_get_drvinfo, 301 .get_ethtool_stats = fjes_get_ethtool_stats, 302 .get_strings = fjes_get_strings, 303 .get_sset_count = fjes_get_sset_count, 304 .get_regs = fjes_get_regs, 305 .get_regs_len = fjes_get_regs_len, 306 .set_dump = fjes_set_dump, 307 .get_dump_flag = fjes_get_dump_flag, 308 .get_dump_data = fjes_get_dump_data, 309 }; 310 311 void fjes_set_ethtool_ops(struct net_device *netdev) 312 { 313 netdev->ethtool_ops = &fjes_ethtool_ops; 314 } 315