linux/drivers/net/ethernet/apm/xgene/xgene_enet_ethtool.c
<<
>>
Prefs
   1/* Applied Micro X-Gene SoC Ethernet Driver
   2 *
   3 * Copyright (c) 2014, Applied Micro Circuits Corporation
   4 * Authors: Iyappan Subramanian <isubramanian@apm.com>
   5 *
   6 * This program is free software; you can redistribute  it and/or modify it
   7 * under  the terms of  the GNU General  Public License as published by the
   8 * Free Software Foundation;  either version 2 of the  License, or (at your
   9 * option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/ethtool.h>
  21#include "xgene_enet_main.h"
  22
  23struct xgene_gstrings_stats {
  24        char name[ETH_GSTRING_LEN];
  25        int offset;
  26        u32 addr;
  27        u32 mask;
  28};
  29
  30#define XGENE_STAT(m) { #m, offsetof(struct rtnl_link_stats64, m) }
  31#define XGENE_EXTD_STAT(s, a, m)                \
  32                {                       \
  33                .name = #s,             \
  34                .addr = a ## _ADDR,     \
  35                .mask = m               \
  36                }
  37
  38static const struct xgene_gstrings_stats gstrings_stats[] = {
  39        XGENE_STAT(rx_packets),
  40        XGENE_STAT(tx_packets),
  41        XGENE_STAT(rx_bytes),
  42        XGENE_STAT(tx_bytes),
  43        XGENE_STAT(rx_errors),
  44        XGENE_STAT(tx_errors),
  45        XGENE_STAT(rx_length_errors),
  46        XGENE_STAT(rx_crc_errors),
  47        XGENE_STAT(rx_frame_errors),
  48        XGENE_STAT(rx_fifo_errors)
  49};
  50
  51static const struct xgene_gstrings_stats gstrings_extd_stats[] = {
  52        XGENE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64, 31),
  53        XGENE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127, 31),
  54        XGENE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255, 31),
  55        XGENE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511, 31),
  56        XGENE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K, 31),
  57        XGENE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX, 31),
  58        XGENE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV, 31),
  59        XGENE_EXTD_STAT(rx_fcs_error_cntr, RFCS, 16),
  60        XGENE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA, 31),
  61        XGENE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA, 31),
  62        XGENE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF, 16),
  63        XGENE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF, 16),
  64        XGENE_EXTD_STAT(rx_unk_opcode_cntr, RXUO, 16),
  65        XGENE_EXTD_STAT(rx_align_err_cntr, RALN, 16),
  66        XGENE_EXTD_STAT(rx_frame_len_err_cntr, RFLR, 16),
  67        XGENE_EXTD_STAT(rx_frame_len_err_recov_cntr, DUMP, 0),
  68        XGENE_EXTD_STAT(rx_code_err_cntr, RCDE, 16),
  69        XGENE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE, 16),
  70        XGENE_EXTD_STAT(rx_undersize_pkt_cntr, RUND, 16),
  71        XGENE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR, 16),
  72        XGENE_EXTD_STAT(rx_fragments_cntr, RFRG, 16),
  73        XGENE_EXTD_STAT(rx_jabber_cntr, RJBR, 16),
  74        XGENE_EXTD_STAT(rx_jabber_recov_cntr, DUMP, 0),
  75        XGENE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP, 16),
  76        XGENE_EXTD_STAT(rx_overrun_cntr, DUMP, 0),
  77        XGENE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA, 31),
  78        XGENE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA, 31),
  79        XGENE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF, 16),
  80        XGENE_EXTD_STAT(tx_defer_pkt_cntr, TDFR, 31),
  81        XGENE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF, 31),
  82        XGENE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL, 31),
  83        XGENE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL, 31),
  84        XGENE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL, 31),
  85        XGENE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL, 31),
  86        XGENE_EXTD_STAT(tx_total_col_cntr, TNCL, 31),
  87        XGENE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH, 16),
  88        XGENE_EXTD_STAT(tx_drop_frame_cntr, TDRP, 16),
  89        XGENE_EXTD_STAT(tx_jabber_frame_cntr, TJBR, 12),
  90        XGENE_EXTD_STAT(tx_fcs_error_cntr, TFCS, 12),
  91        XGENE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF, 12),
  92        XGENE_EXTD_STAT(tx_oversize_frame_cntr, TOVR, 12),
  93        XGENE_EXTD_STAT(tx_undersize_frame_cntr, TUND, 12),
  94        XGENE_EXTD_STAT(tx_fragments_cntr, TFRG, 12),
  95        XGENE_EXTD_STAT(tx_underrun_cntr, DUMP, 0)
  96};
  97
  98#define XGENE_STATS_LEN         ARRAY_SIZE(gstrings_stats)
  99#define XGENE_EXTD_STATS_LEN    ARRAY_SIZE(gstrings_extd_stats)
 100#define RFCS_IDX                7
 101#define RALN_IDX                13
 102#define RFLR_IDX                14
 103#define FALSE_RFLR_IDX          15
 104#define RUND_IDX                18
 105#define FALSE_RJBR_IDX          22
 106#define RX_OVERRUN_IDX          24
 107#define TFCS_IDX                38
 108#define TFRG_IDX                42
 109#define TX_UNDERRUN_IDX         43
 110
 111static void xgene_get_drvinfo(struct net_device *ndev,
 112                              struct ethtool_drvinfo *info)
 113{
 114        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 115        struct platform_device *pdev = pdata->pdev;
 116
 117        strcpy(info->driver, "xgene_enet");
 118        strcpy(info->version, XGENE_DRV_VERSION);
 119        snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A");
 120        sprintf(info->bus_info, "%s", pdev->name);
 121}
 122
 123static int xgene_get_link_ksettings(struct net_device *ndev,
 124                                    struct ethtool_link_ksettings *cmd)
 125{
 126        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 127        struct phy_device *phydev = ndev->phydev;
 128        u32 supported;
 129
 130        if (phy_interface_mode_is_rgmii(pdata->phy_mode)) {
 131                if (phydev == NULL)
 132                        return -ENODEV;
 133
 134                phy_ethtool_ksettings_get(phydev, cmd);
 135
 136                return 0;
 137        } else if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
 138                if (pdata->mdio_driver) {
 139                        if (!phydev)
 140                                return -ENODEV;
 141
 142                        phy_ethtool_ksettings_get(phydev, cmd);
 143
 144                        return 0;
 145                }
 146
 147                supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
 148                        SUPPORTED_MII;
 149                ethtool_convert_legacy_u32_to_link_mode(
 150                        cmd->link_modes.supported,
 151                        supported);
 152                ethtool_convert_legacy_u32_to_link_mode(
 153                        cmd->link_modes.advertising,
 154                        supported);
 155
 156                cmd->base.speed = SPEED_1000;
 157                cmd->base.duplex = DUPLEX_FULL;
 158                cmd->base.port = PORT_MII;
 159                cmd->base.autoneg = AUTONEG_ENABLE;
 160        } else {
 161                supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE;
 162                ethtool_convert_legacy_u32_to_link_mode(
 163                        cmd->link_modes.supported,
 164                        supported);
 165                ethtool_convert_legacy_u32_to_link_mode(
 166                        cmd->link_modes.advertising,
 167                        supported);
 168
 169                cmd->base.speed = SPEED_10000;
 170                cmd->base.duplex = DUPLEX_FULL;
 171                cmd->base.port = PORT_FIBRE;
 172                cmd->base.autoneg = AUTONEG_DISABLE;
 173        }
 174
 175        return 0;
 176}
 177
 178static int xgene_set_link_ksettings(struct net_device *ndev,
 179                                    const struct ethtool_link_ksettings *cmd)
 180{
 181        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 182        struct phy_device *phydev = ndev->phydev;
 183
 184        if (phy_interface_mode_is_rgmii(pdata->phy_mode)) {
 185                if (!phydev)
 186                        return -ENODEV;
 187
 188                return phy_ethtool_ksettings_set(phydev, cmd);
 189        }
 190
 191        if (pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
 192                if (pdata->mdio_driver) {
 193                        if (!phydev)
 194                                return -ENODEV;
 195
 196                        return phy_ethtool_ksettings_set(phydev, cmd);
 197                }
 198        }
 199
 200        return -EINVAL;
 201}
 202
 203static void xgene_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
 204{
 205        int i;
 206        u8 *p = data;
 207
 208        if (stringset != ETH_SS_STATS)
 209                return;
 210
 211        for (i = 0; i < XGENE_STATS_LEN; i++) {
 212                memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN);
 213                p += ETH_GSTRING_LEN;
 214        }
 215
 216        for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) {
 217                memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN);
 218                p += ETH_GSTRING_LEN;
 219        }
 220}
 221
 222static int xgene_get_sset_count(struct net_device *ndev, int sset)
 223{
 224        if (sset != ETH_SS_STATS)
 225                return -EINVAL;
 226
 227        return XGENE_STATS_LEN + XGENE_EXTD_STATS_LEN;
 228}
 229
 230static void xgene_get_extd_stats(struct xgene_enet_pdata *pdata)
 231{
 232        u32 rx_drop, tx_drop;
 233        u32 mask, tmp;
 234        int i;
 235
 236        for (i = 0; i < XGENE_EXTD_STATS_LEN; i++) {
 237                tmp = xgene_enet_rd_stat(pdata, gstrings_extd_stats[i].addr);
 238                if (gstrings_extd_stats[i].mask) {
 239                        mask = GENMASK(gstrings_extd_stats[i].mask - 1, 0);
 240                        pdata->extd_stats[i] += (tmp & mask);
 241                }
 242        }
 243
 244        if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) {
 245                /* Errata 10GE_10 - SW should intepret RALN as 0 */
 246                pdata->extd_stats[RALN_IDX] = 0;
 247        } else {
 248                /* Errata ENET_15 - Fixes RFCS, RFLR, TFCS counter */
 249                pdata->extd_stats[RFCS_IDX] -= pdata->extd_stats[RALN_IDX];
 250                pdata->extd_stats[RFLR_IDX] -= pdata->extd_stats[RUND_IDX];
 251                pdata->extd_stats[TFCS_IDX] -= pdata->extd_stats[TFRG_IDX];
 252        }
 253
 254        pdata->mac_ops->get_drop_cnt(pdata, &rx_drop, &tx_drop);
 255        pdata->extd_stats[RX_OVERRUN_IDX] += rx_drop;
 256        pdata->extd_stats[TX_UNDERRUN_IDX] += tx_drop;
 257
 258        /* Errata 10GE_8 -  Update Frame recovered from Errata 10GE_8/ENET_11 */
 259        pdata->extd_stats[FALSE_RFLR_IDX] = pdata->false_rflr;
 260        /* Errata ENET_15 - Jabber Frame recov'ed from Errata 10GE_10/ENET_15 */
 261        pdata->extd_stats[FALSE_RJBR_IDX] = pdata->vlan_rjbr;
 262}
 263
 264int xgene_extd_stats_init(struct xgene_enet_pdata *pdata)
 265{
 266        pdata->extd_stats = devm_kmalloc_array(&pdata->pdev->dev,
 267                        XGENE_EXTD_STATS_LEN, sizeof(u64), GFP_KERNEL);
 268        if (!pdata->extd_stats)
 269                return -ENOMEM;
 270
 271        xgene_get_extd_stats(pdata);
 272        memset(pdata->extd_stats, 0, XGENE_EXTD_STATS_LEN * sizeof(u64));
 273
 274        return 0;
 275}
 276
 277static void xgene_get_ethtool_stats(struct net_device *ndev,
 278                                    struct ethtool_stats *dummy,
 279                                    u64 *data)
 280{
 281        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 282        struct rtnl_link_stats64 stats;
 283        int i;
 284
 285        dev_get_stats(ndev, &stats);
 286        for (i = 0; i < XGENE_STATS_LEN; i++)
 287                data[i] = *(u64 *)((char *)&stats + gstrings_stats[i].offset);
 288
 289        xgene_get_extd_stats(pdata);
 290        for (i = 0; i < XGENE_EXTD_STATS_LEN; i++)
 291                data[i + XGENE_STATS_LEN] = pdata->extd_stats[i];
 292}
 293
 294static void xgene_get_pauseparam(struct net_device *ndev,
 295                                 struct ethtool_pauseparam *pp)
 296{
 297        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 298
 299        pp->autoneg = pdata->pause_autoneg;
 300        pp->tx_pause = pdata->tx_pause;
 301        pp->rx_pause = pdata->rx_pause;
 302}
 303
 304static int xgene_set_pauseparam(struct net_device *ndev,
 305                                struct ethtool_pauseparam *pp)
 306{
 307        struct xgene_enet_pdata *pdata = netdev_priv(ndev);
 308        struct phy_device *phydev = ndev->phydev;
 309        u32 oldadv, newadv;
 310
 311        if (phy_interface_mode_is_rgmii(pdata->phy_mode) ||
 312            pdata->phy_mode == PHY_INTERFACE_MODE_SGMII) {
 313                if (!phydev)
 314                        return -EINVAL;
 315
 316                if (!(phydev->supported & SUPPORTED_Pause) ||
 317                    (!(phydev->supported & SUPPORTED_Asym_Pause) &&
 318                     pp->rx_pause != pp->tx_pause))
 319                        return -EINVAL;
 320
 321                pdata->pause_autoneg = pp->autoneg;
 322                pdata->tx_pause = pp->tx_pause;
 323                pdata->rx_pause = pp->rx_pause;
 324
 325                oldadv = phydev->advertising;
 326                newadv = oldadv & ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
 327
 328                if (pp->rx_pause)
 329                        newadv |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
 330
 331                if (pp->tx_pause)
 332                        newadv ^= ADVERTISED_Asym_Pause;
 333
 334                if (oldadv ^ newadv) {
 335                        phydev->advertising = newadv;
 336
 337                        if (phydev->autoneg)
 338                                return phy_start_aneg(phydev);
 339
 340                        if (!pp->autoneg) {
 341                                pdata->mac_ops->flowctl_tx(pdata,
 342                                                           pdata->tx_pause);
 343                                pdata->mac_ops->flowctl_rx(pdata,
 344                                                           pdata->rx_pause);
 345                        }
 346                }
 347
 348        } else {
 349                if (pp->autoneg)
 350                        return -EINVAL;
 351
 352                pdata->tx_pause = pp->tx_pause;
 353                pdata->rx_pause = pp->rx_pause;
 354
 355                pdata->mac_ops->flowctl_tx(pdata, pdata->tx_pause);
 356                pdata->mac_ops->flowctl_rx(pdata, pdata->rx_pause);
 357        }
 358
 359        return 0;
 360}
 361
 362static const struct ethtool_ops xgene_ethtool_ops = {
 363        .get_drvinfo = xgene_get_drvinfo,
 364        .get_link = ethtool_op_get_link,
 365        .get_strings = xgene_get_strings,
 366        .get_sset_count = xgene_get_sset_count,
 367        .get_ethtool_stats = xgene_get_ethtool_stats,
 368        .get_link_ksettings = xgene_get_link_ksettings,
 369        .set_link_ksettings = xgene_set_link_ksettings,
 370        .get_pauseparam = xgene_get_pauseparam,
 371        .set_pauseparam = xgene_set_pauseparam
 372};
 373
 374void xgene_enet_set_ethtool_ops(struct net_device *ndev)
 375{
 376        ndev->ethtool_ops = &xgene_ethtool_ops;
 377}
 378