Skip to content

Commit 9b17010

Browse files
habetsm-xilinxdavem330
authored andcommitted
sfc: Add ethtool -m support for QSFP modules
This also adds support for non-QSFP modules attached to QSFP. Signed-off-by: Martin Habets <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent bb4d991 commit 9b17010

File tree

1 file changed

+181
-43
lines changed

1 file changed

+181
-43
lines changed

drivers/net/ethernet/sfc/mcdi_port.c

Lines changed: 181 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -746,59 +746,171 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx,
746746
return NULL;
747747
}
748748

749-
#define SFP_PAGE_SIZE 128
750-
#define SFP_NUM_PAGES 2
751-
static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
752-
struct ethtool_eeprom *ee, u8 *data)
749+
#define SFP_PAGE_SIZE 128
750+
#define SFF_DIAG_TYPE_OFFSET 92
751+
#define SFF_DIAG_ADDR_CHANGE BIT(2)
752+
#define SFF_8079_NUM_PAGES 2
753+
#define SFF_8472_NUM_PAGES 4
754+
#define SFF_8436_NUM_PAGES 5
755+
#define SFF_DMT_LEVEL_OFFSET 94
756+
757+
/** efx_mcdi_phy_get_module_eeprom_page() - Get a single page of module eeprom
758+
* @efx: NIC context
759+
* @page: EEPROM page number
760+
* @data: Destination data pointer
761+
* @offset: Offset in page to copy from in to data
762+
* @space: Space available in data
763+
*
764+
* Return:
765+
* >=0 - amount of data copied
766+
* <0 - error
767+
*/
768+
static int efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx,
769+
unsigned int page,
770+
u8 *data, ssize_t offset,
771+
ssize_t space)
753772
{
754773
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX);
755774
MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN);
756775
size_t outlen;
757-
int rc;
758776
unsigned int payload_len;
759-
unsigned int space_remaining = ee->len;
760-
unsigned int page;
761-
unsigned int page_off;
762777
unsigned int to_copy;
763-
u8 *user_data = data;
778+
int rc;
764779

765-
BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN);
780+
if (offset > SFP_PAGE_SIZE)
781+
return -EINVAL;
766782

767-
page_off = ee->offset % SFP_PAGE_SIZE;
768-
page = ee->offset / SFP_PAGE_SIZE;
783+
to_copy = min(space, SFP_PAGE_SIZE - offset);
769784

770-
while (space_remaining && (page < SFP_NUM_PAGES)) {
771-
MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
785+
MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page);
786+
rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO,
787+
inbuf, sizeof(inbuf),
788+
outbuf, sizeof(outbuf),
789+
&outlen);
772790

773-
rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO,
774-
inbuf, sizeof(inbuf),
775-
outbuf, sizeof(outbuf),
776-
&outlen);
777-
if (rc)
778-
return rc;
791+
if (rc)
792+
return rc;
793+
794+
if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST +
795+
SFP_PAGE_SIZE))
796+
return -EIO;
797+
798+
payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN);
799+
if (payload_len != SFP_PAGE_SIZE)
800+
return -EIO;
779801

780-
if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST +
781-
SFP_PAGE_SIZE))
782-
return -EIO;
802+
memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
803+
to_copy);
783804

784-
payload_len = MCDI_DWORD(outbuf,
785-
GET_PHY_MEDIA_INFO_OUT_DATALEN);
786-
if (payload_len != SFP_PAGE_SIZE)
787-
return -EIO;
805+
return to_copy;
806+
}
788807

789-
/* Copy as much as we can into data */
790-
payload_len -= page_off;
791-
to_copy = (space_remaining < payload_len) ?
792-
space_remaining : payload_len;
808+
static int efx_mcdi_phy_get_module_eeprom_byte(struct efx_nic *efx,
809+
unsigned int page,
810+
u8 byte)
811+
{
812+
int rc;
813+
u8 data;
793814

794-
memcpy(user_data,
795-
MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + page_off,
796-
to_copy);
815+
rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, &data, byte, 1);
816+
if (rc == 1)
817+
return data;
818+
819+
return rc;
820+
}
821+
822+
static int efx_mcdi_phy_diag_type(struct efx_nic *efx)
823+
{
824+
/* Page zero of the EEPROM includes the diagnostic type at byte 92. */
825+
return efx_mcdi_phy_get_module_eeprom_byte(efx, 0,
826+
SFF_DIAG_TYPE_OFFSET);
827+
}
797828

798-
space_remaining -= to_copy;
799-
user_data += to_copy;
800-
page_off = 0;
801-
page++;
829+
static int efx_mcdi_phy_sff_8472_level(struct efx_nic *efx)
830+
{
831+
/* Page zero of the EEPROM includes the DMT level at byte 94. */
832+
return efx_mcdi_phy_get_module_eeprom_byte(efx, 0,
833+
SFF_DMT_LEVEL_OFFSET);
834+
}
835+
836+
static u32 efx_mcdi_phy_module_type(struct efx_nic *efx)
837+
{
838+
struct efx_mcdi_phy_data *phy_data = efx->phy_data;
839+
840+
if (phy_data->media != MC_CMD_MEDIA_QSFP_PLUS)
841+
return phy_data->media;
842+
843+
/* A QSFP+ NIC may actually have an SFP+ module attached.
844+
* The ID is page 0, byte 0.
845+
*/
846+
switch (efx_mcdi_phy_get_module_eeprom_byte(efx, 0, 0)) {
847+
case 0x3:
848+
return MC_CMD_MEDIA_SFP_PLUS;
849+
case 0xc:
850+
case 0xd:
851+
return MC_CMD_MEDIA_QSFP_PLUS;
852+
default:
853+
return 0;
854+
}
855+
}
856+
857+
static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
858+
struct ethtool_eeprom *ee, u8 *data)
859+
{
860+
int rc;
861+
ssize_t space_remaining = ee->len;
862+
unsigned int page_off;
863+
bool ignore_missing;
864+
int num_pages;
865+
int page;
866+
867+
switch (efx_mcdi_phy_module_type(efx)) {
868+
case MC_CMD_MEDIA_SFP_PLUS:
869+
num_pages = efx_mcdi_phy_sff_8472_level(efx) > 0 ?
870+
SFF_8472_NUM_PAGES : SFF_8079_NUM_PAGES;
871+
page = 0;
872+
ignore_missing = false;
873+
break;
874+
case MC_CMD_MEDIA_QSFP_PLUS:
875+
num_pages = SFF_8436_NUM_PAGES;
876+
page = -1; /* We obtain the lower page by asking for -1. */
877+
ignore_missing = true; /* Ignore missing pages after page 0. */
878+
break;
879+
default:
880+
return -EOPNOTSUPP;
881+
}
882+
883+
page_off = ee->offset % SFP_PAGE_SIZE;
884+
page += ee->offset / SFP_PAGE_SIZE;
885+
886+
while (space_remaining && (page < num_pages)) {
887+
rc = efx_mcdi_phy_get_module_eeprom_page(efx, page,
888+
data, page_off,
889+
space_remaining);
890+
891+
if (rc > 0) {
892+
space_remaining -= rc;
893+
data += rc;
894+
page_off = 0;
895+
page++;
896+
} else if (rc == 0) {
897+
space_remaining = 0;
898+
} else if (ignore_missing && (page > 0)) {
899+
int intended_size = SFP_PAGE_SIZE - page_off;
900+
901+
space_remaining -= intended_size;
902+
if (space_remaining < 0) {
903+
space_remaining = 0;
904+
} else {
905+
memset(data, 0, intended_size);
906+
data += intended_size;
907+
page_off = 0;
908+
page++;
909+
rc = 0;
910+
}
911+
} else {
912+
return rc;
913+
}
802914
}
803915

804916
return 0;
@@ -807,16 +919,42 @@ static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx,
807919
static int efx_mcdi_phy_get_module_info(struct efx_nic *efx,
808920
struct ethtool_modinfo *modinfo)
809921
{
810-
struct efx_mcdi_phy_data *phy_cfg = efx->phy_data;
922+
int sff_8472_level;
923+
int diag_type;
811924

812-
switch (phy_cfg->media) {
925+
switch (efx_mcdi_phy_module_type(efx)) {
813926
case MC_CMD_MEDIA_SFP_PLUS:
814-
modinfo->type = ETH_MODULE_SFF_8079;
815-
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
816-
return 0;
927+
sff_8472_level = efx_mcdi_phy_sff_8472_level(efx);
928+
929+
/* If we can't read the diagnostics level we have none. */
930+
if (sff_8472_level < 0)
931+
return -EOPNOTSUPP;
932+
933+
/* Check if this module requires the (unsupported) address
934+
* change operation.
935+
*/
936+
diag_type = efx_mcdi_phy_diag_type(efx);
937+
938+
if ((sff_8472_level == 0) ||
939+
(diag_type & SFF_DIAG_ADDR_CHANGE)) {
940+
modinfo->type = ETH_MODULE_SFF_8079;
941+
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
942+
} else {
943+
modinfo->type = ETH_MODULE_SFF_8472;
944+
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
945+
}
946+
break;
947+
948+
case MC_CMD_MEDIA_QSFP_PLUS:
949+
modinfo->type = ETH_MODULE_SFF_8436;
950+
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
951+
break;
952+
817953
default:
818954
return -EOPNOTSUPP;
819955
}
956+
957+
return 0;
820958
}
821959

822960
static const struct efx_phy_operations efx_mcdi_phy_ops = {

0 commit comments

Comments
 (0)