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