Loading drivers/net/ethernet/sfc/mcdi_port.c +181 −43 Original line number Diff line number Diff line Loading @@ -747,33 +747,47 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx, } #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 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; page_off = ee->offset % SFP_PAGE_SIZE; page = ee->offset / SFP_PAGE_SIZE; to_copy = min(space, SFP_PAGE_SIZE - offset); while (space_remaining && (page < SFP_NUM_PAGES)) { MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO, rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc) return rc; Loading @@ -781,24 +795,122 @@ static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, SFP_PAGE_SIZE)) return -EIO; payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN); 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, memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset, to_copy); space_remaining -= to_copy; user_data += 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; while (space_remaining && (page < num_pages)) { rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, data, page_off, space_remaining); 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; } } return 0; Loading @@ -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: 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; return 0; } 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 Loading
drivers/net/ethernet/sfc/mcdi_port.c +181 −43 Original line number Diff line number Diff line Loading @@ -747,33 +747,47 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx, } #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 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; page_off = ee->offset % SFP_PAGE_SIZE; page = ee->offset / SFP_PAGE_SIZE; to_copy = min(space, SFP_PAGE_SIZE - offset); while (space_remaining && (page < SFP_NUM_PAGES)) { MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO, rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc) return rc; Loading @@ -781,24 +795,122 @@ static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, SFP_PAGE_SIZE)) return -EIO; payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN); 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, memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset, to_copy); space_remaining -= to_copy; user_data += 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; while (space_remaining && (page < num_pages)) { rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, data, page_off, space_remaining); 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; } } return 0; Loading @@ -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: 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; return 0; } 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