mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 01:51:34 +00:00
sfc: move more MCDI port code
Various functions dealing with flow control, forward error correction, polling, port number, and PHY testing. Signed-off-by: Alexandru-Mihai Maftei <amaftei@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1a8d88a8b7
commit
1cf0f76ada
@ -188,58 +188,6 @@ int efx_mcdi_port_reconfigure(struct efx_nic *efx)
|
|||||||
efx->loopback_mode, 0);
|
efx->loopback_mode, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
rmtadv = 0;
|
|
||||||
if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
|
|
||||||
rmtadv |= ADVERTISED_Pause;
|
|
||||||
if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
|
|
||||||
rmtadv |= ADVERTISED_Asym_Pause;
|
|
||||||
|
|
||||||
if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause)
|
|
||||||
netif_err(efx, link, efx->net_dev,
|
|
||||||
"warning: link partner doesn't support pause frames");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool efx_mcdi_phy_poll(struct efx_nic *efx)
|
|
||||||
{
|
|
||||||
struct efx_link_state old_state = efx->link_state;
|
|
||||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN);
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
WARN_ON(!mutex_is_locked(&efx->mac_lock));
|
|
||||||
|
|
||||||
BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
|
|
||||||
|
|
||||||
rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
|
|
||||||
outbuf, sizeof(outbuf), NULL);
|
|
||||||
if (rc)
|
|
||||||
efx->link_state.up = false;
|
|
||||||
else
|
|
||||||
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));
|
|
||||||
|
|
||||||
return !efx_link_state_equal(&efx->link_state, &old_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void efx_mcdi_phy_remove(struct efx_nic *efx)
|
static void efx_mcdi_phy_remove(struct efx_nic *efx)
|
||||||
{
|
{
|
||||||
struct efx_mcdi_phy_data *phy_data = efx->phy_data;
|
struct efx_mcdi_phy_data *phy_data = efx->phy_data;
|
||||||
@ -327,57 +275,6 @@ efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int efx_mcdi_phy_get_fecparam(struct efx_nic *efx, struct ethtool_fecparam *fec)
|
|
||||||
{
|
|
||||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_V2_LEN);
|
|
||||||
u32 caps, active, speed; /* MCDI format */
|
|
||||||
bool is_25g = false;
|
|
||||||
size_t outlen;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
|
|
||||||
rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
|
|
||||||
outbuf, sizeof(outbuf), &outlen);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
if (outlen < MC_CMD_GET_LINK_OUT_V2_LEN)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int efx_mcdi_phy_set_fecparam(struct efx_nic *efx,
|
static int efx_mcdi_phy_set_fecparam(struct efx_nic *efx,
|
||||||
const struct ethtool_fecparam *fec)
|
const struct ethtool_fecparam *fec)
|
||||||
{
|
{
|
||||||
@ -405,27 +302,6 @@ static int efx_mcdi_phy_set_fecparam(struct efx_nic *efx,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int efx_mcdi_phy_test_alive(struct efx_nic *efx)
|
|
||||||
{
|
|
||||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_STATE_OUT_LEN);
|
|
||||||
size_t outlen;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
BUILD_BUG_ON(MC_CMD_GET_PHY_STATE_IN_LEN != 0);
|
|
||||||
|
|
||||||
rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_STATE, NULL, 0,
|
|
||||||
outbuf, sizeof(outbuf), &outlen);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (outlen < MC_CMD_GET_PHY_STATE_OUT_LEN)
|
|
||||||
return -EIO;
|
|
||||||
if (MCDI_DWORD(outbuf, GET_PHY_STATE_OUT_STATE) != MC_CMD_PHY_STATE_OK)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *const mcdi_sft9001_cable_diag_names[] = {
|
static const char *const mcdi_sft9001_cable_diag_names[] = {
|
||||||
"cable.pairA.length",
|
"cable.pairA.length",
|
||||||
"cable.pairB.length",
|
"cable.pairB.length",
|
||||||
@ -1008,17 +884,3 @@ void efx_mcdi_port_remove(struct efx_nic *efx)
|
|||||||
efx->phy_op->remove(efx);
|
efx->phy_op->remove(efx);
|
||||||
efx_nic_free_buffer(efx, &efx->stats_buffer);
|
efx_nic_free_buffer(efx, &efx->stats_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
rc = efx_mcdi_rpc(efx, MC_CMD_GET_PORT_ASSIGNMENT, NULL, 0,
|
|
||||||
outbuf, sizeof(outbuf), NULL);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
return MCDI_DWORD(outbuf, GET_PORT_ASSIGNMENT_OUT_PORT);
|
|
||||||
}
|
|
||||||
|
@ -348,3 +348,142 @@ u32 mcdi_fec_caps_to_ethtool(u32 caps, bool is_25g)
|
|||||||
(baser_req ? ETHTOOL_FEC_BASER : 0) |
|
(baser_req ? ETHTOOL_FEC_BASER : 0) |
|
||||||
(baser == baser_req && rs == rs_req ? 0 : ETHTOOL_FEC_AUTO);
|
(baser == baser_req && rs == rs_req ? 0 : ETHTOOL_FEC_AUTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
rmtadv = 0;
|
||||||
|
if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
|
||||||
|
rmtadv |= ADVERTISED_Pause;
|
||||||
|
if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
|
||||||
|
rmtadv |= ADVERTISED_Asym_Pause;
|
||||||
|
|
||||||
|
if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause)
|
||||||
|
netif_err(efx, link, efx->net_dev,
|
||||||
|
"warning: link partner doesn't support pause frames");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool efx_mcdi_phy_poll(struct efx_nic *efx)
|
||||||
|
{
|
||||||
|
struct efx_link_state old_state = efx->link_state;
|
||||||
|
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
WARN_ON(!mutex_is_locked(&efx->mac_lock));
|
||||||
|
|
||||||
|
BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
|
||||||
|
|
||||||
|
rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
|
||||||
|
outbuf, sizeof(outbuf), NULL);
|
||||||
|
if (rc)
|
||||||
|
efx->link_state.up = false;
|
||||||
|
else
|
||||||
|
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));
|
||||||
|
|
||||||
|
return !efx_link_state_equal(&efx->link_state, &old_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
int efx_mcdi_phy_get_fecparam(struct efx_nic *efx, struct ethtool_fecparam *fec)
|
||||||
|
{
|
||||||
|
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_V2_LEN);
|
||||||
|
u32 caps, active, speed; /* MCDI format */
|
||||||
|
bool is_25g = false;
|
||||||
|
size_t outlen;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0);
|
||||||
|
rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0,
|
||||||
|
outbuf, sizeof(outbuf), &outlen);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
if (outlen < MC_CMD_GET_LINK_OUT_V2_LEN)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int efx_mcdi_phy_test_alive(struct efx_nic *efx)
|
||||||
|
{
|
||||||
|
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_STATE_OUT_LEN);
|
||||||
|
size_t outlen;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(MC_CMD_GET_PHY_STATE_IN_LEN != 0);
|
||||||
|
|
||||||
|
rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_STATE, NULL, 0,
|
||||||
|
outbuf, sizeof(outbuf), &outlen);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (outlen < MC_CMD_GET_PHY_STATE_OUT_LEN)
|
||||||
|
return -EIO;
|
||||||
|
if (MCDI_DWORD(outbuf, GET_PHY_STATE_OUT_STATE) != MC_CMD_PHY_STATE_OK)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
rc = efx_mcdi_rpc(efx, MC_CMD_GET_PORT_ASSIGNMENT, NULL, 0,
|
||||||
|
outbuf, sizeof(outbuf), NULL);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return MCDI_DWORD(outbuf, GET_PORT_ASSIGNMENT_OUT_PORT);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user