// SPDX-License-Identifier: GPL-2.0-only /**************************************************************************** * Driver for Solarflare network controllers and boards * Copyright 2018 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation, incorporated herein by reference.
*/
bitmap_zero(linkset, __ETHTOOL_LINK_MODE_MASK_NBITS); switch (media) { case MC_CMD_MEDIA_KX4:
SET_BIT(Backplane); if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
SET_BIT(1000baseKX_Full); if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
SET_BIT(10000baseKX4_Full); if (cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN))
SET_BIT(40000baseKR4_Full); break;
case MC_CMD_MEDIA_XFP: case MC_CMD_MEDIA_SFP_PLUS: case MC_CMD_MEDIA_QSFP_PLUS:
SET_BIT(FIBRE); if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) {
SET_BIT(1000baseT_Full);
SET_BIT(1000baseX_Full);
} if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) {
SET_BIT(10000baseCR_Full);
SET_BIT(10000baseLR_Full);
SET_BIT(10000baseSR_Full);
} if (cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN)) {
SET_BIT(40000baseCR4_Full);
SET_BIT(40000baseSR4_Full);
} if (cap & (1 << MC_CMD_PHY_CAP_100000FDX_LBN)) {
SET_BIT(100000baseCR4_Full);
SET_BIT(100000baseSR4_Full);
} if (cap & (1 << MC_CMD_PHY_CAP_25000FDX_LBN)) {
SET_BIT(25000baseCR_Full);
SET_BIT(25000baseSR_Full);
} if (cap & (1 << MC_CMD_PHY_CAP_50000FDX_LBN))
SET_BIT(50000baseCR2_Full); break;
case MC_CMD_MEDIA_BASE_T:
SET_BIT(TP); if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
SET_BIT(10baseT_Half); if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
SET_BIT(10baseT_Full); if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
SET_BIT(100baseT_Half); if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
SET_BIT(100baseT_Full); if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
SET_BIT(1000baseT_Half); if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
SET_BIT(1000baseT_Full); if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
SET_BIT(10000baseT_Full); break;
}
if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
SET_BIT(Pause); if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
SET_BIT(Asym_Pause); if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
SET_BIT(Autoneg);
#undef SET_BIT
}
u32 ethtool_linkset_to_mcdi_cap(constunsignedlong *linkset)
{
u32 result = 0;
#define TEST_BIT(name) test_bit(ETHTOOL_LINK_MODE_ ## name ## _BIT, \
linkset)
if (TEST_BIT(10baseT_Half))
result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN); if (TEST_BIT(10baseT_Full))
result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN); if (TEST_BIT(100baseT_Half))
result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN); if (TEST_BIT(100baseT_Full))
result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN); if (TEST_BIT(1000baseT_Half))
result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN); if (TEST_BIT(1000baseT_Full) || TEST_BIT(1000baseKX_Full) ||
TEST_BIT(1000baseX_Full))
result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN); if (TEST_BIT(10000baseT_Full) || TEST_BIT(10000baseKX4_Full) ||
TEST_BIT(10000baseCR_Full) || TEST_BIT(10000baseLR_Full) ||
TEST_BIT(10000baseSR_Full))
result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN); if (TEST_BIT(40000baseCR4_Full) || TEST_BIT(40000baseKR4_Full) ||
TEST_BIT(40000baseSR4_Full))
result |= (1 << MC_CMD_PHY_CAP_40000FDX_LBN); if (TEST_BIT(100000baseCR4_Full) || TEST_BIT(100000baseSR4_Full))
result |= (1 << MC_CMD_PHY_CAP_100000FDX_LBN); if (TEST_BIT(25000baseCR_Full) || TEST_BIT(25000baseSR_Full))
result |= (1 << MC_CMD_PHY_CAP_25000FDX_LBN); if (TEST_BIT(50000baseCR2_Full))
result |= (1 << MC_CMD_PHY_CAP_50000FDX_LBN); if (TEST_BIT(Pause))
result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN); if (TEST_BIT(Asym_Pause))
result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN); if (TEST_BIT(Autoneg))
result |= (1 << MC_CMD_PHY_CAP_AN_LBN);
/* The semantics of the ethtool FEC mode bitmask are not well defined, * particularly the meaning of combinations of bits. Which means we get to * define our own semantics, as follows: * OFF overrides any other bits, and means "disable all FEC" (with the * exception of 25G KR4/CR4, where it is not possible to reject it if AN * partner requests it). * AUTO on its own means use cable requirements and link partner autoneg with * fw-default preferences for the cable type. * AUTO and either RS or BASER means use the specified FEC type if cable and * link partner support it, otherwise autoneg/fw-default. * RS or BASER alone means use the specified FEC type if cable and link partner * support it and either requests it, otherwise no FEC. * Both RS and BASER (whether AUTO or not) means use FEC if cable and link * partner support it, preferring RS to BASER.
*/
u32 ethtool_fec_caps_to_mcdi(u32 supported_cap, u32 ethtool_cap)
{
u32 ret = 0;
if (ethtool_cap & ETHTOOL_FEC_OFF) return 0;
if (ethtool_cap & ETHTOOL_FEC_AUTO)
ret |= ((1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) & supported_cap; if (ethtool_cap & ETHTOOL_FEC_RS &&
supported_cap & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN))
ret |= (1 << MC_CMD_PHY_CAP_RS_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN); if (ethtool_cap & ETHTOOL_FEC_BASER) { if (supported_cap & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN))
ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN); if (supported_cap & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN))
ret |= (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) |
(1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN);
} return ret;
}
/* Invert ethtool_fec_caps_to_mcdi. There are two combinations that function * can never produce, (baser xor rs) and neither req; the implementation below * maps both of those to AUTO. This should never matter, and it's not clear * what a better mapping would be anyway.
*/
u32 mcdi_fec_caps_to_ethtool(u32 caps, bool is_25g)
{ bool rs = caps & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN),
rs_req = caps & (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN),
baser = is_25g ? caps & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN)
: caps & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN),
baser_req = is_25g ? caps & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN)
: caps & (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN);
/* Verify that the forced flow control settings (!EFX_FC_AUTO) are * supported by the link partner. Warn the user if this isn't the case
*/ void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa)
{ struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
u32 rmtadv;
/* The link partner capabilities are only relevant if the * link supports flow control autonegotiation
*/ if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) return;
/* If flow control autoneg is supported and enabled, then fine */ if (efx->wanted_fc & EFX_FC_AUTO) return;
rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes); if (rc != 0) goto fail; /* The MC indicates that LOOPBACK_NONE is a valid loopback mode, * but by convention we don't
*/
efx->loopback_modes &= ~(1 << LOOPBACK_NONE);
/* Set the initial link mode */
efx_mcdi_phy_decode_link(efx, &efx->link_state,
MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED),
MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS),
MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL));
/* Record the initial FEC configuration (or nearest approximation * representable in the ethtool configuration space)
*/
efx->fec_config = mcdi_fec_caps_to_ethtool(caps,
efx->link_state.speed == 25000 ||
efx->link_state.speed == 50000);
/* Default to Autonegotiated flow control if the PHY supports it */
efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
efx->wanted_fc |= EFX_FC_AUTO;
efx_link_set_wanted_fc(efx, efx->wanted_fc);
/* behaviour for 25G/50G links depends on 25G BASER bit */
speed = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_LINK_SPEED);
is_25g = speed == 25000 || speed == 50000;
caps = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_CAP);
fec->fec = mcdi_fec_caps_to_ethtool(caps, is_25g); /* BASER is never supported on 100G */ if (speed == 100000)
fec->fec &= ~ETHTOOL_FEC_BASER;
active = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_FEC_TYPE); switch (active) { case MC_CMD_FEC_NONE:
fec->active_fec = ETHTOOL_FEC_OFF; break; case MC_CMD_FEC_BASER:
fec->active_fec = ETHTOOL_FEC_BASER; break; case MC_CMD_FEC_RS:
fec->active_fec = ETHTOOL_FEC_RS; break; default:
netif_warn(efx, hw, efx->net_dev, "Firmware reports unrecognised FEC_TYPE %u\n",
active); /* We don't know what firmware has picked. AUTO is as good a * "can't happen" value as any other.
*/
fec->active_fec = ETHTOOL_FEC_AUTO; break;
}
return 0;
}
/* Basic validation to ensure that the caps we are going to attempt to set are * in fact supported by the adapter. Note that 'no FEC' is always supported.
*/ staticint ethtool_fec_supported(u32 supported_cap, u32 ethtool_cap)
{ if (ethtool_cap & ETHTOOL_FEC_OFF) return 0;
if (ethtool_cap &&
!ethtool_fec_caps_to_mcdi(supported_cap, ethtool_cap)) return -EINVAL; return 0;
}
int efx_mcdi_phy_set_fecparam(struct efx_nic *efx, conststruct ethtool_fecparam *fec)
{ struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
u32 caps; int rc;
rc = ethtool_fec_supported(phy_cfg->supported_cap, fec->fec); if (rc) return rc;
/* Work out what efx_mcdi_phy_set_link_ksettings() would produce from * saved advertising bits
*/ if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, efx->link_advertising))
caps = (ethtool_linkset_to_mcdi_cap(efx->link_advertising) |
1 << MC_CMD_PHY_CAP_AN_LBN); else
caps = phy_cfg->forced_cap;
/* SFT9001 specific cable diagnostics output */ if (efx->phy_type == PHY_TYPE_SFT9001B &&
(bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT ||
bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) {
ptr = MCDI_PTR(outbuf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A); if (status == MC_CMD_POLL_BIST_PASSED &&
outlen >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN) { for (i = 0; i < 8; i++) {
results[count + i] =
EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i],
EFX_DWORD_0);
}
}
count += 8;
}
rc = count;
out: return rc;
}
int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results, unsignedint flags)
{ struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
u32 mode; int rc;
if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) {
rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results); if (rc < 0) return rc;
results += rc;
}
/* If we support both LONG and SHORT, then run each in response to * break or not. Otherwise, run the one we support
*/
mode = 0; if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN)) { if ((flags & ETH_TEST_FL_OFFLINE) &&
(phy_cfg->flags &
(1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN)))
mode = MC_CMD_PHY_BIST_CABLE_LONG; else
mode = MC_CMD_PHY_BIST_CABLE_SHORT;
} elseif (phy_cfg->flags &
(1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))
mode = MC_CMD_PHY_BIST_CABLE_LONG;
if (mode != 0) {
rc = efx_mcdi_bist(efx, mode, results); if (rc < 0) return rc;
results += rc;
}
/** efx_mcdi_phy_get_module_eeprom_page() - Get a single page of module eeprom * @efx: NIC context * @page: EEPROM page number * @data: Destination data pointer * @offset: Offset in page to copy from in to data * @space: Space available in data * * Return: * >=0 - amount of data copied * <0 - error
*/ staticint efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx, unsignedint page,
u8 *data, ssize_t offset,
ssize_t space)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX);
MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN); unsignedint payload_len; unsignedint to_copy;
size_t outlen; int rc;
staticint efx_mcdi_phy_diag_type(struct efx_nic *efx)
{ /* Page zero of the EEPROM includes the diagnostic type at byte 92. */ return efx_mcdi_phy_get_module_eeprom_byte(efx, 0,
SFF_DIAG_TYPE_OFFSET);
}
staticint efx_mcdi_phy_sff_8472_level(struct efx_nic *efx)
{ /* Page zero of the EEPROM includes the DMT level at byte 94. */ return efx_mcdi_phy_get_module_eeprom_byte(efx, 0,
SFF_DMT_LEVEL_OFFSET);
}
if (phy_data->media != MC_CMD_MEDIA_QSFP_PLUS) return phy_data->media;
/* A QSFP+ NIC may actually have an SFP+ module attached. * The ID is page 0, byte 0. * QSFP28 is of type SFF_8636, however, this is treated * the same by ethtool, so we can also treat them the same.
*/ switch (efx_mcdi_phy_get_module_eeprom_byte(efx, 0, 0)) { case 0x3: /* SFP */ return MC_CMD_MEDIA_SFP_PLUS; case 0xc: /* QSFP */ case 0xd: /* QSFP+ */ case 0x11: /* QSFP28 */ return MC_CMD_MEDIA_QSFP_PLUS; default: return 0;
}
}
int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, struct ethtool_eeprom *ee, u8 *data)
{ int rc;
ssize_t space_remaining = ee->len; unsignedint page_off; bool ignore_missing; int num_pages; int page;
switch (efx_mcdi_phy_module_type(efx)) { case MC_CMD_MEDIA_SFP_PLUS:
num_pages = efx_mcdi_phy_sff_8472_level(efx) > 0 ?
SFF_8472_NUM_PAGES : SFF_8079_NUM_PAGES;
page = 0;
ignore_missing = false; break; case MC_CMD_MEDIA_QSFP_PLUS:
num_pages = SFF_8436_NUM_PAGES;
page = -1; /* We obtain the lower page by asking for -1. */
ignore_missing = true; /* Ignore missing pages after page 0. */ break; default: return -EOPNOTSUPP;
}
/* Get physical port number (EF10 only; on Siena it is same as PF number) */ int efx_mcdi_port_get_number(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PORT_ASSIGNMENT_OUT_LEN); int rc;
/* efx->link_state is only modified by efx_mcdi_phy_get_link(), * which is only run after flushing the event queues. Therefore, it * is safe to modify the link state outside of the mac_lock here.
*/
efx_mcdi_phy_decode_link(efx, &efx->link_state, speed, flags, fcntl);
efx_mcdi_phy_check_fcntl(efx, lpa);
efx_link_status_changed(efx);
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.