sfc: Add ethtool -m support for QSFP modules
This also adds support for non-QSFP modules attached to QSFP. Signed-off-by: Martin Habets <mhabets@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bb4d991a28
commit
9b17010da5
@ -746,59 +746,171 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SFP_PAGE_SIZE 128
|
#define SFP_PAGE_SIZE 128
|
||||||
#define SFP_NUM_PAGES 2
|
#define SFF_DIAG_TYPE_OFFSET 92
|
||||||
static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
|
#define SFF_DIAG_ADDR_CHANGE BIT(2)
|
||||||
struct ethtool_eeprom *ee, u8 *data)
|
#define SFF_8079_NUM_PAGES 2
|
||||||
|
#define SFF_8472_NUM_PAGES 4
|
||||||
|
#define SFF_8436_NUM_PAGES 5
|
||||||
|
#define SFF_DMT_LEVEL_OFFSET 94
|
||||||
|
|
||||||
|
/** 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
|
||||||
|
*/
|
||||||
|
static int efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx,
|
||||||
|
unsigned int page,
|
||||||
|
u8 *data, ssize_t offset,
|
||||||
|
ssize_t space)
|
||||||
{
|
{
|
||||||
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX);
|
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX);
|
||||||
MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN);
|
MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN);
|
||||||
size_t outlen;
|
size_t outlen;
|
||||||
int rc;
|
|
||||||
unsigned int payload_len;
|
unsigned int payload_len;
|
||||||
unsigned int space_remaining = ee->len;
|
|
||||||
unsigned int page;
|
|
||||||
unsigned int page_off;
|
|
||||||
unsigned int to_copy;
|
unsigned int to_copy;
|
||||||
u8 *user_data = data;
|
int rc;
|
||||||
|
|
||||||
BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN);
|
if (offset > SFP_PAGE_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
to_copy = min(space, SFP_PAGE_SIZE - offset);
|
||||||
|
|
||||||
|
MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
|
||||||
|
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO,
|
||||||
|
inbuf, sizeof(inbuf),
|
||||||
|
outbuf, sizeof(outbuf),
|
||||||
|
&outlen);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST +
|
||||||
|
SFP_PAGE_SIZE))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN);
|
||||||
|
if (payload_len != SFP_PAGE_SIZE)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
|
||||||
|
to_copy);
|
||||||
|
|
||||||
|
return to_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int efx_mcdi_phy_get_module_eeprom_byte(struct efx_nic *efx,
|
||||||
|
unsigned int page,
|
||||||
|
u8 byte)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
u8 data;
|
||||||
|
|
||||||
|
rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, &data, byte, 1);
|
||||||
|
if (rc == 1)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 efx_mcdi_phy_module_type(struct efx_nic *efx)
|
||||||
|
{
|
||||||
|
struct efx_mcdi_phy_data *phy_data = efx->phy_data;
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
switch (efx_mcdi_phy_get_module_eeprom_byte(efx, 0, 0)) {
|
||||||
|
case 0x3:
|
||||||
|
return MC_CMD_MEDIA_SFP_PLUS;
|
||||||
|
case 0xc:
|
||||||
|
case 0xd:
|
||||||
|
return MC_CMD_MEDIA_QSFP_PLUS;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
unsigned int 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;
|
||||||
|
}
|
||||||
|
|
||||||
page_off = ee->offset % SFP_PAGE_SIZE;
|
page_off = ee->offset % SFP_PAGE_SIZE;
|
||||||
page = ee->offset / SFP_PAGE_SIZE;
|
page += ee->offset / SFP_PAGE_SIZE;
|
||||||
|
|
||||||
while (space_remaining && (page < SFP_NUM_PAGES)) {
|
while (space_remaining && (page < num_pages)) {
|
||||||
MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
|
rc = efx_mcdi_phy_get_module_eeprom_page(efx, page,
|
||||||
|
data, page_off,
|
||||||
|
space_remaining);
|
||||||
|
|
||||||
rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO,
|
if (rc > 0) {
|
||||||
inbuf, sizeof(inbuf),
|
space_remaining -= rc;
|
||||||
outbuf, sizeof(outbuf),
|
data += rc;
|
||||||
&outlen);
|
page_off = 0;
|
||||||
if (rc)
|
page++;
|
||||||
|
} else if (rc == 0) {
|
||||||
|
space_remaining = 0;
|
||||||
|
} else if (ignore_missing && (page > 0)) {
|
||||||
|
int intended_size = SFP_PAGE_SIZE - page_off;
|
||||||
|
|
||||||
|
space_remaining -= intended_size;
|
||||||
|
if (space_remaining < 0) {
|
||||||
|
space_remaining = 0;
|
||||||
|
} else {
|
||||||
|
memset(data, 0, intended_size);
|
||||||
|
data += intended_size;
|
||||||
|
page_off = 0;
|
||||||
|
page++;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return rc;
|
return rc;
|
||||||
|
}
|
||||||
if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST +
|
|
||||||
SFP_PAGE_SIZE))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
payload_len = MCDI_DWORD(outbuf,
|
|
||||||
GET_PHY_MEDIA_INFO_OUT_DATALEN);
|
|
||||||
if (payload_len != SFP_PAGE_SIZE)
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
/* Copy as much as we can into data */
|
|
||||||
payload_len -= page_off;
|
|
||||||
to_copy = (space_remaining < payload_len) ?
|
|
||||||
space_remaining : payload_len;
|
|
||||||
|
|
||||||
memcpy(user_data,
|
|
||||||
MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + page_off,
|
|
||||||
to_copy);
|
|
||||||
|
|
||||||
space_remaining -= to_copy;
|
|
||||||
user_data += to_copy;
|
|
||||||
page_off = 0;
|
|
||||||
page++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -807,16 +919,42 @@ static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
|
|||||||
static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
|
static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
|
||||||
struct ethtool_modinfo *modinfo)
|
struct ethtool_modinfo *modinfo)
|
||||||
{
|
{
|
||||||
struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
|
int sff_8472_level;
|
||||||
|
int diag_type;
|
||||||
|
|
||||||
switch (phy_cfg->media) {
|
switch (efx_mcdi_phy_module_type(efx)) {
|
||||||
case MC_CMD_MEDIA_SFP_PLUS:
|
case MC_CMD_MEDIA_SFP_PLUS:
|
||||||
modinfo->type = ETH_MODULE_SFF_8079;
|
sff_8472_level = efx_mcdi_phy_sff_8472_level(efx);
|
||||||
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
|
|
||||||
return 0;
|
/* If we can't read the diagnostics level we have none. */
|
||||||
|
if (sff_8472_level < 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* Check if this module requires the (unsupported) address
|
||||||
|
* change operation.
|
||||||
|
*/
|
||||||
|
diag_type = efx_mcdi_phy_diag_type(efx);
|
||||||
|
|
||||||
|
if ((sff_8472_level == 0) ||
|
||||||
|
(diag_type & SFF_DIAG_ADDR_CHANGE)) {
|
||||||
|
modinfo->type = ETH_MODULE_SFF_8079;
|
||||||
|
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
|
||||||
|
} else {
|
||||||
|
modinfo->type = ETH_MODULE_SFF_8472;
|
||||||
|
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MC_CMD_MEDIA_QSFP_PLUS:
|
||||||
|
modinfo->type = ETH_MODULE_SFF_8436;
|
||||||
|
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct efx_phy_operations efx_mcdi_phy_ops = {
|
static const struct efx_phy_operations efx_mcdi_phy_ops = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user