linux/drivers/net/ethernet/sfc/mcdi_phy.c
<<
>>
Prefs
   1/****************************************************************************
   2 * Driver for Solarflare Solarstorm network controllers and boards
   3 * Copyright 2009-2010 Solarflare Communications Inc.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License version 2 as published
   7 * by the Free Software Foundation, incorporated herein by reference.
   8 */
   9
  10/*
  11 * Driver for PHY related operations via MCDI.
  12 */
  13
  14#include <linux/slab.h>
  15#include "efx.h"
  16#include "phy.h"
  17#include "mcdi.h"
  18#include "mcdi_pcol.h"
  19#include "nic.h"
  20#include "selftest.h"
  21
  22struct efx_mcdi_phy_data {
  23        u32 flags;
  24        u32 type;
  25        u32 supported_cap;
  26        u32 channel;
  27        u32 port;
  28        u32 stats_mask;
  29        u8 name[20];
  30        u32 media;
  31        u32 mmd_mask;
  32        u8 revision[20];
  33        u32 forced_cap;
  34};
  35
  36static int
  37efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_data *cfg)
  38{
  39        u8 outbuf[MC_CMD_GET_PHY_CFG_OUT_LEN];
  40        size_t outlen;
  41        int rc;
  42
  43        BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0);
  44        BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name));
  45
  46        rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0,
  47                          outbuf, sizeof(outbuf), &outlen);
  48        if (rc)
  49                goto fail;
  50
  51        if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) {
  52                rc = -EIO;
  53                goto fail;
  54        }
  55
  56        cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS);
  57        cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE);
  58        cfg->supported_cap =
  59                MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP);
  60        cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL);
  61        cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT);
  62        cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK);
  63        memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME),
  64               sizeof(cfg->name));
  65        cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE);
  66        cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK);
  67        memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION),
  68               sizeof(cfg->revision));
  69
  70        return 0;
  71
  72fail:
  73        netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
  74        return rc;
  75}
  76
  77static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities,
  78                             u32 flags, u32 loopback_mode,
  79                             u32 loopback_speed)
  80{
  81        u8 inbuf[MC_CMD_SET_LINK_IN_LEN];
  82        int rc;
  83
  84        BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0);
  85
  86        MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities);
  87        MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags);
  88        MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode);
  89        MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed);
  90
  91        rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf),
  92                          NULL, 0, NULL);
  93        if (rc)
  94                goto fail;
  95
  96        return 0;
  97
  98fail:
  99        netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
 100        return rc;
 101}
 102
 103static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes)
 104{
 105        u8 outbuf[MC_CMD_GET_LOOPBACK_MODES_OUT_LEN];
 106        size_t outlen;
 107        int rc;
 108
 109        rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, 0,
 110                          outbuf, sizeof(outbuf), &outlen);
 111        if (rc)
 112                goto fail;
 113
 114        if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) {
 115                rc = -EIO;
 116                goto fail;
 117        }
 118
 119        *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_OUT_SUGGESTED);
 120
 121        return 0;
 122
 123fail:
 124        netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
 125        return rc;
 126}
 127
 128int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus,
 129                         unsigned int prtad, unsigned int devad, u16 addr,
 130                         u16 *value_out, u32 *status_out)
 131{
 132        u8 inbuf[MC_CMD_MDIO_READ_IN_LEN];
 133        u8 outbuf[MC_CMD_MDIO_READ_OUT_LEN];
 134        size_t outlen;
 135        int rc;
 136
 137        MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus);
 138        MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad);
 139        MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad);
 140        MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr);
 141
 142        rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf),
 143                          outbuf, sizeof(outbuf), &outlen);
 144        if (rc)
 145                goto fail;
 146
 147        *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE);
 148        *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS);
 149        return 0;
 150
 151fail:
 152        netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
 153        return rc;
 154}
 155
 156int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus,
 157                          unsigned int prtad, unsigned int devad, u16 addr,
 158                          u16 value, u32 *status_out)
 159{
 160        u8 inbuf[MC_CMD_MDIO_WRITE_IN_LEN];
 161        u8 outbuf[MC_CMD_MDIO_WRITE_OUT_LEN];
 162        size_t outlen;
 163        int rc;
 164
 165        MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus);
 166        MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad);
 167        MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad);
 168        MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr);
 169        MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value);
 170
 171        rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf),
 172                          outbuf, sizeof(outbuf), &outlen);
 173        if (rc)
 174                goto fail;
 175
 176        *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS);
 177        return 0;
 178
 179fail:
 180        netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
 181        return rc;
 182}
 183
 184static u32 mcdi_to_ethtool_cap(u32 media, u32 cap)
 185{
 186        u32 result = 0;
 187
 188        switch (media) {
 189        case MC_CMD_MEDIA_KX4:
 190                result |= SUPPORTED_Backplane;
 191                if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
 192                        result |= SUPPORTED_1000baseKX_Full;
 193                if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
 194                        result |= SUPPORTED_10000baseKX4_Full;
 195                break;
 196
 197        case MC_CMD_MEDIA_XFP:
 198        case MC_CMD_MEDIA_SFP_PLUS:
 199                result |= SUPPORTED_FIBRE;
 200                break;
 201
 202        case MC_CMD_MEDIA_BASE_T:
 203                result |= SUPPORTED_TP;
 204                if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
 205                        result |= SUPPORTED_10baseT_Half;
 206                if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
 207                        result |= SUPPORTED_10baseT_Full;
 208                if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
 209                        result |= SUPPORTED_100baseT_Half;
 210                if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
 211                        result |= SUPPORTED_100baseT_Full;
 212                if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
 213                        result |= SUPPORTED_1000baseT_Half;
 214                if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
 215                        result |= SUPPORTED_1000baseT_Full;
 216                if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
 217                        result |= SUPPORTED_10000baseT_Full;
 218                break;
 219        }
 220
 221        if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
 222                result |= SUPPORTED_Pause;
 223        if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
 224                result |= SUPPORTED_Asym_Pause;
 225        if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
 226                result |= SUPPORTED_Autoneg;
 227
 228        return result;
 229}
 230
 231static u32 ethtool_to_mcdi_cap(u32 cap)
 232{
 233        u32 result = 0;
 234
 235        if (cap & SUPPORTED_10baseT_Half)
 236                result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN);
 237        if (cap & SUPPORTED_10baseT_Full)
 238                result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN);
 239        if (cap & SUPPORTED_100baseT_Half)
 240                result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN);
 241        if (cap & SUPPORTED_100baseT_Full)
 242                result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN);
 243        if (cap & SUPPORTED_1000baseT_Half)
 244                result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN);
 245        if (cap & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseKX_Full))
 246                result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN);
 247        if (cap & (SUPPORTED_10000baseT_Full | SUPPORTED_10000baseKX4_Full))
 248                result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN);
 249        if (cap & SUPPORTED_Pause)
 250                result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN);
 251        if (cap & SUPPORTED_Asym_Pause)
 252                result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN);
 253        if (cap & SUPPORTED_Autoneg)
 254                result |= (1 << MC_CMD_PHY_CAP_AN_LBN);
 255
 256        return result;
 257}
 258
 259static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx)
 260{
 261        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 262        enum efx_phy_mode mode, supported;
 263        u32 flags;
 264
 265        /* TODO: Advertise the capabilities supported by this PHY */
 266        supported = 0;
 267        if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_TXDIS_LBN))
 268                supported |= PHY_MODE_TX_DISABLED;
 269        if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_LBN))
 270                supported |= PHY_MODE_LOW_POWER;
 271        if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_POWEROFF_LBN))
 272                supported |= PHY_MODE_OFF;
 273
 274        mode = efx->phy_mode & supported;
 275
 276        flags = 0;
 277        if (mode & PHY_MODE_TX_DISABLED)
 278                flags |= (1 << MC_CMD_SET_LINK_IN_TXDIS_LBN);
 279        if (mode & PHY_MODE_LOW_POWER)
 280                flags |= (1 << MC_CMD_SET_LINK_IN_LOWPOWER_LBN);
 281        if (mode & PHY_MODE_OFF)
 282                flags |= (1 << MC_CMD_SET_LINK_IN_POWEROFF_LBN);
 283
 284        return flags;
 285}
 286
 287static u32 mcdi_to_ethtool_media(u32 media)
 288{
 289        switch (media) {
 290        case MC_CMD_MEDIA_XAUI:
 291        case MC_CMD_MEDIA_CX4:
 292        case MC_CMD_MEDIA_KX4:
 293                return PORT_OTHER;
 294
 295        case MC_CMD_MEDIA_XFP:
 296        case MC_CMD_MEDIA_SFP_PLUS:
 297                return PORT_FIBRE;
 298
 299        case MC_CMD_MEDIA_BASE_T:
 300                return PORT_TP;
 301
 302        default:
 303                return PORT_OTHER;
 304        }
 305}
 306
 307static int efx_mcdi_phy_probe(struct efx_nic *efx)
 308{
 309        struct efx_mcdi_phy_data *phy_data;
 310        u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
 311        u32 caps;
 312        int rc;
 313
 314        /* Initialise and populate phy_data */
 315        phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL);
 316        if (phy_data == NULL)
 317                return -ENOMEM;
 318
 319        rc = efx_mcdi_get_phy_cfg(efx, phy_data);
 320        if (rc != 0)
 321                goto fail;
 322
 323        /* Read initial link advertisement */
 324        BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
 325        rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
 326                          outbuf, sizeof(outbuf), NULL);
 327        if (rc)
 328                goto fail;
 329
 330        /* Fill out nic state */
 331        efx->phy_data = phy_data;
 332        efx->phy_type = phy_data->type;
 333
 334        efx->mdio_bus = phy_data->channel;
 335        efx->mdio.prtad = phy_data->port;
 336        efx->mdio.mmds = phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22);
 337        efx->mdio.mode_support = 0;
 338        if (phy_data->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22))
 339                efx->mdio.mode_support |= MDIO_SUPPORTS_C22;
 340        if (phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22))
 341                efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22;
 342
 343        caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP);
 344        if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN))
 345                efx->link_advertising =
 346                        mcdi_to_ethtool_cap(phy_data->media, caps);
 347        else
 348                phy_data->forced_cap = caps;
 349
 350        /* Assert that we can map efx -> mcdi loopback modes */
 351        BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE);
 352        BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA);
 353        BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC);
 354        BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII);
 355        BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS);
 356        BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI);
 357        BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII);
 358        BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII);
 359        BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR);
 360        BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI);
 361        BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR);
 362        BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR);
 363        BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR);
 364        BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR);
 365        BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY);
 366        BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS);
 367        BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS);
 368        BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD);
 369        BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT);
 370        BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS);
 371        BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS);
 372        BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR);
 373        BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR);
 374        BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS);
 375        BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS);
 376        BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR);
 377        BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS);
 378
 379        rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes);
 380        if (rc != 0)
 381                goto fail;
 382        /* The MC indicates that LOOPBACK_NONE is a valid loopback mode,
 383         * but by convention we don't */
 384        efx->loopback_modes &= ~(1 << LOOPBACK_NONE);
 385
 386        /* Set the initial link mode */
 387        efx_mcdi_phy_decode_link(
 388                efx, &efx->link_state,
 389                MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
 390                MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
 391                MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
 392
 393        /* Default to Autonegotiated flow control if the PHY supports it */
 394        efx->wanted_fc = EFX_FC_RX | EFX_FC_TX;
 395        if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
 396                efx->wanted_fc |= EFX_FC_AUTO;
 397        efx_link_set_wanted_fc(efx, efx->wanted_fc);
 398
 399        return 0;
 400
 401fail:
 402        kfree(phy_data);
 403        return rc;
 404}
 405
 406int efx_mcdi_phy_reconfigure(struct efx_nic *efx)
 407{
 408        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 409        u32 caps = (efx->link_advertising ?
 410                    ethtool_to_mcdi_cap(efx->link_advertising) :
 411                    phy_cfg->forced_cap);
 412
 413        return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
 414                                 efx->loopback_mode, 0);
 415}
 416
 417void efx_mcdi_phy_decode_link(struct efx_nic *efx,
 418                              struct efx_link_state *link_state,
 419                              u32 speed, u32 flags, u32 fcntl)
 420{
 421        switch (fcntl) {
 422        case MC_CMD_FCNTL_AUTO:
 423                WARN_ON(1);     /* This is not a link mode */
 424                link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX;
 425                break;
 426        case MC_CMD_FCNTL_BIDIR:
 427                link_state->fc = EFX_FC_TX | EFX_FC_RX;
 428                break;
 429        case MC_CMD_FCNTL_RESPOND:
 430                link_state->fc = EFX_FC_RX;
 431                break;
 432        default:
 433                WARN_ON(1);
 434        case MC_CMD_FCNTL_OFF:
 435                link_state->fc = 0;
 436                break;
 437        }
 438
 439        link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN));
 440        link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN));
 441        link_state->speed = speed;
 442}
 443
 444/* Verify that the forced flow control settings (!EFX_FC_AUTO) are
 445 * supported by the link partner. Warn the user if this isn't the case
 446 */
 447void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa)
 448{
 449        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 450        u32 rmtadv;
 451
 452        /* The link partner capabilities are only relevant if the
 453         * link supports flow control autonegotiation */
 454        if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
 455                return;
 456
 457        /* If flow control autoneg is supported and enabled, then fine */
 458        if (efx->wanted_fc & EFX_FC_AUTO)
 459                return;
 460
 461        rmtadv = 0;
 462        if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
 463                rmtadv |= ADVERTISED_Pause;
 464        if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
 465                rmtadv |=  ADVERTISED_Asym_Pause;
 466
 467        if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause)
 468                netif_err(efx, link, efx->net_dev,
 469                          "warning: link partner doesn't support pause frames");
 470}
 471
 472static bool efx_mcdi_phy_poll(struct efx_nic *efx)
 473{
 474        struct efx_link_state old_state = efx->link_state;
 475        u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
 476        int rc;
 477
 478        WARN_ON(!mutex_is_locked(&efx->mac_lock));
 479
 480        BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
 481
 482        rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
 483                          outbuf, sizeof(outbuf), NULL);
 484        if (rc) {
 485                netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n",
 486                          __func__, rc);
 487                efx->link_state.up = false;
 488        } else {
 489                efx_mcdi_phy_decode_link(
 490                        efx, &efx->link_state,
 491                        MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
 492                        MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
 493                        MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
 494        }
 495
 496        return !efx_link_state_equal(&efx->link_state, &old_state);
 497}
 498
 499static void efx_mcdi_phy_remove(struct efx_nic *efx)
 500{
 501        struct efx_mcdi_phy_data *phy_data = efx->phy_data;
 502
 503        efx->phy_data = NULL;
 504        kfree(phy_data);
 505}
 506
 507static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
 508{
 509        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 510        u8 outbuf[MC_CMD_GET_LINK_OUT_LEN];
 511        int rc;
 512
 513        ecmd->supported =
 514                mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap);
 515        ecmd->advertising = efx->link_advertising;
 516        ethtool_cmd_speed_set(ecmd, efx->link_state.speed);
 517        ecmd->duplex = efx->link_state.fd;
 518        ecmd->port = mcdi_to_ethtool_media(phy_cfg->media);
 519        ecmd->phy_address = phy_cfg->port;
 520        ecmd->transceiver = XCVR_INTERNAL;
 521        ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg);
 522        ecmd->mdio_support = (efx->mdio.mode_support &
 523                              (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22));
 524
 525        BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
 526        rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
 527                          outbuf, sizeof(outbuf), NULL);
 528        if (rc) {
 529                netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n",
 530                          __func__, rc);
 531                return;
 532        }
 533        ecmd->lp_advertising =
 534                mcdi_to_ethtool_cap(phy_cfg->media,
 535                                    MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP));
 536}
 537
 538static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd)
 539{
 540        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 541        u32 caps;
 542        int rc;
 543
 544        if (ecmd->autoneg) {
 545                caps = (ethtool_to_mcdi_cap(ecmd->advertising) |
 546                         1 << MC_CMD_PHY_CAP_AN_LBN);
 547        } else if (ecmd->duplex) {
 548                switch (ethtool_cmd_speed(ecmd)) {
 549                case 10:    caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN;    break;
 550                case 100:   caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN;   break;
 551                case 1000:  caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN;  break;
 552                case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break;
 553                default:    return -EINVAL;
 554                }
 555        } else {
 556                switch (ethtool_cmd_speed(ecmd)) {
 557                case 10:    caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN;    break;
 558                case 100:   caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN;   break;
 559                case 1000:  caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN;  break;
 560                default:    return -EINVAL;
 561                }
 562        }
 563
 564        rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx),
 565                               efx->loopback_mode, 0);
 566        if (rc)
 567                return rc;
 568
 569        if (ecmd->autoneg) {
 570                efx_link_set_advertising(
 571                        efx, ecmd->advertising | ADVERTISED_Autoneg);
 572                phy_cfg->forced_cap = 0;
 573        } else {
 574                efx_link_set_advertising(efx, 0);
 575                phy_cfg->forced_cap = caps;
 576        }
 577        return 0;
 578}
 579
 580static int efx_mcdi_phy_test_alive(struct efx_nic *efx)
 581{
 582        u8 outbuf[MC_CMD_GET_PHY_STATE_OUT_LEN];
 583        size_t outlen;
 584        int rc;
 585
 586        BUILD_BUG_ON(MC_CMD_GET_PHY_STATE_IN_LEN != 0);
 587
 588        rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_STATE, NULL, 0,
 589                          outbuf, sizeof(outbuf), &outlen);
 590        if (rc)
 591                return rc;
 592
 593        if (outlen < MC_CMD_GET_PHY_STATE_OUT_LEN)
 594                return -EIO;
 595        if (MCDI_DWORD(outbuf, GET_PHY_STATE_OUT_STATE) != MC_CMD_PHY_STATE_OK)
 596                return -EINVAL;
 597
 598        return 0;
 599}
 600
 601static const char *const mcdi_sft9001_cable_diag_names[] = {
 602        "cable.pairA.length",
 603        "cable.pairB.length",
 604        "cable.pairC.length",
 605        "cable.pairD.length",
 606        "cable.pairA.status",
 607        "cable.pairB.status",
 608        "cable.pairC.status",
 609        "cable.pairD.status",
 610};
 611
 612static int efx_mcdi_bist(struct efx_nic *efx, unsigned int bist_mode,
 613                         int *results)
 614{
 615        unsigned int retry, i, count = 0;
 616        size_t outlen;
 617        u32 status;
 618        u8 *buf, *ptr;
 619        int rc;
 620
 621        buf = kzalloc(0x100, GFP_KERNEL);
 622        if (buf == NULL)
 623                return -ENOMEM;
 624
 625        BUILD_BUG_ON(MC_CMD_START_BIST_OUT_LEN != 0);
 626        MCDI_SET_DWORD(buf, START_BIST_IN_TYPE, bist_mode);
 627        rc = efx_mcdi_rpc(efx, MC_CMD_START_BIST, buf, MC_CMD_START_BIST_IN_LEN,
 628                          NULL, 0, NULL);
 629        if (rc)
 630                goto out;
 631
 632        /* Wait up to 10s for BIST to finish */
 633        for (retry = 0; retry < 100; ++retry) {
 634                BUILD_BUG_ON(MC_CMD_POLL_BIST_IN_LEN != 0);
 635                rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, 0,
 636                                  buf, 0x100, &outlen);
 637                if (rc)
 638                        goto out;
 639
 640                status = MCDI_DWORD(buf, POLL_BIST_OUT_RESULT);
 641                if (status != MC_CMD_POLL_BIST_RUNNING)
 642                        goto finished;
 643
 644                msleep(100);
 645        }
 646
 647        rc = -ETIMEDOUT;
 648        goto out;
 649
 650finished:
 651        results[count++] = (status == MC_CMD_POLL_BIST_PASSED) ? 1 : -1;
 652
 653        /* SFT9001 specific cable diagnostics output */
 654        if (efx->phy_type == PHY_TYPE_SFT9001B &&
 655            (bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT ||
 656             bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) {
 657                ptr = MCDI_PTR(buf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A);
 658                if (status == MC_CMD_POLL_BIST_PASSED &&
 659                    outlen >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN) {
 660                        for (i = 0; i < 8; i++) {
 661                                results[count + i] =
 662                                        EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i],
 663                                                        EFX_DWORD_0);
 664                        }
 665                }
 666                count += 8;
 667        }
 668        rc = count;
 669
 670out:
 671        kfree(buf);
 672
 673        return rc;
 674}
 675
 676static int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results,
 677                                  unsigned flags)
 678{
 679        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 680        u32 mode;
 681        int rc;
 682
 683        if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
 684                rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results);
 685                if (rc < 0)
 686                        return rc;
 687
 688                results += rc;
 689        }
 690
 691        /* If we support both LONG and SHORT, then run each in response to
 692         * break or not. Otherwise, run the one we support */
 693        mode = 0;
 694        if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN)) {
 695                if ((flags & ETH_TEST_FL_OFFLINE) &&
 696                    (phy_cfg->flags &
 697                     (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN)))
 698                        mode = MC_CMD_PHY_BIST_CABLE_LONG;
 699                else
 700                        mode = MC_CMD_PHY_BIST_CABLE_SHORT;
 701        } else if (phy_cfg->flags &
 702                   (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))
 703                mode = MC_CMD_PHY_BIST_CABLE_LONG;
 704
 705        if (mode != 0) {
 706                rc = efx_mcdi_bist(efx, mode, results);
 707                if (rc < 0)
 708                        return rc;
 709                results += rc;
 710        }
 711
 712        return 0;
 713}
 714
 715static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
 716                                          unsigned int index)
 717{
 718        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 719
 720        if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
 721                if (index == 0)
 722                        return "bist";
 723                --index;
 724        }
 725
 726        if (phy_cfg->flags & ((1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN) |
 727                              (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))) {
 728                if (index == 0)
 729                        return "cable";
 730                --index;
 731
 732                if (efx->phy_type == PHY_TYPE_SFT9001B) {
 733                        if (index < ARRAY_SIZE(mcdi_sft9001_cable_diag_names))
 734                                return mcdi_sft9001_cable_diag_names[index];
 735                        index -= ARRAY_SIZE(mcdi_sft9001_cable_diag_names);
 736                }
 737        }
 738
 739        return NULL;
 740}
 741
 742#define SFP_PAGE_SIZE   128
 743#define SFP_NUM_PAGES   2
 744static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
 745                                          struct ethtool_eeprom *ee, u8 *data)
 746{
 747        u8 outbuf[MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX];
 748        u8 inbuf[MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN];
 749        size_t outlen;
 750        int rc;
 751        unsigned int payload_len;
 752        unsigned int space_remaining = ee->len;
 753        unsigned int page;
 754        unsigned int page_off;
 755        unsigned int to_copy;
 756        u8 *user_data = data;
 757
 758        BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN);
 759
 760        page_off = ee->offset % SFP_PAGE_SIZE;
 761        page = ee->offset / SFP_PAGE_SIZE;
 762
 763        while (space_remaining && (page < SFP_NUM_PAGES)) {
 764                MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
 765
 766                rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO,
 767                                  inbuf, sizeof(inbuf),
 768                                  outbuf, sizeof(outbuf),
 769                                  &outlen);
 770                if (rc)
 771                        return rc;
 772
 773                if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST +
 774                              SFP_PAGE_SIZE))
 775                        return -EIO;
 776
 777                payload_len = MCDI_DWORD(outbuf,
 778                                         GET_PHY_MEDIA_INFO_OUT_DATALEN);
 779                if (payload_len != SFP_PAGE_SIZE)
 780                        return -EIO;
 781
 782                /* Copy as much as we can into data */
 783                payload_len -= page_off;
 784                to_copy = (space_remaining < payload_len) ?
 785                        space_remaining : payload_len;
 786
 787                memcpy(user_data,
 788                       outbuf + page_off +
 789                       MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST,
 790                       to_copy);
 791
 792                space_remaining -= to_copy;
 793                user_data += to_copy;
 794                page_off = 0;
 795                page++;
 796        }
 797
 798        return 0;
 799}
 800
 801static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
 802                                        struct ethtool_modinfo *modinfo)
 803{
 804        struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
 805
 806        switch (phy_cfg->media) {
 807        case MC_CMD_MEDIA_SFP_PLUS:
 808                modinfo->type = ETH_MODULE_SFF_8079;
 809                modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
 810                return 0;
 811        default:
 812                return -EOPNOTSUPP;
 813        }
 814}
 815
 816const struct efx_phy_operations efx_mcdi_phy_ops = {
 817        .probe          = efx_mcdi_phy_probe,
 818        .init           = efx_port_dummy_op_int,
 819        .reconfigure    = efx_mcdi_phy_reconfigure,
 820        .poll           = efx_mcdi_phy_poll,
 821        .fini           = efx_port_dummy_op_void,
 822        .remove         = efx_mcdi_phy_remove,
 823        .get_settings   = efx_mcdi_phy_get_settings,
 824        .set_settings   = efx_mcdi_phy_set_settings,
 825        .test_alive     = efx_mcdi_phy_test_alive,
 826        .run_tests      = efx_mcdi_phy_run_tests,
 827        .test_name      = efx_mcdi_phy_test_name,
 828        .get_module_eeprom = efx_mcdi_phy_get_module_eeprom,
 829        .get_module_info = efx_mcdi_phy_get_module_info,
 830};
 831