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;
|
||||
}
|
||||
|
||||
#define SFP_PAGE_SIZE 128
|
||||
#define SFP_NUM_PAGES 2
|
||||
static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
|
||||
struct ethtool_eeprom *ee, u8 *data)
|
||||
#define SFP_PAGE_SIZE 128
|
||||
#define SFF_DIAG_TYPE_OFFSET 92
|
||||
#define SFF_DIAG_ADDR_CHANGE BIT(2)
|
||||
#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(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN);
|
||||
size_t outlen;
|
||||
int rc;
|
||||
unsigned int payload_len;
|
||||
unsigned int space_remaining = ee->len;
|
||||
unsigned int page;
|
||||
unsigned int page_off;
|
||||
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 = ee->offset / SFP_PAGE_SIZE;
|
||||
page += ee->offset / SFP_PAGE_SIZE;
|
||||
|
||||
while (space_remaining && (page < SFP_NUM_PAGES)) {
|
||||
MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
|
||||
while (space_remaining && (page < num_pages)) {
|
||||
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,
|
||||
inbuf, sizeof(inbuf),
|
||||
outbuf, sizeof(outbuf),
|
||||
&outlen);
|
||||
if (rc)
|
||||
if (rc > 0) {
|
||||
space_remaining -= rc;
|
||||
data += rc;
|
||||
page_off = 0;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
@ -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,
|
||||
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:
|
||||
modinfo->type = ETH_MODULE_SFF_8079;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
|
||||
return 0;
|
||||
sff_8472_level = efx_mcdi_phy_sff_8472_level(efx);
|
||||
|
||||
/* 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:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct efx_phy_operations efx_mcdi_phy_ops = {
|
||||
|
|
Loading…
Reference in New Issue