linux/drivers/net/fjes/fjes_ethtool.c
<<
>>
Prefs
   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
  31struct 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
  43static 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
  58static 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
 100static 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
 151static 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
 161static 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
 178static 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
 191static 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
 197static 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
 237static 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
 269static 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
 282static 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
 297static 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
 310void fjes_set_ethtool_ops(struct net_device *netdev)
 311{
 312        netdev->ethtool_ops = &fjes_ethtool_ops;
 313}
 314