net/bnxt: add support to dump SFP module info

Message ID 20210609024515.14534-1-kalesh-anakkur.purayil@broadcom.com (mailing list archive)
State Accepted, archived
Delegated to: Ajit Khaparde
Headers
Series net/bnxt: add support to dump SFP module info |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/Intel-compilation warning apply issues
ci/iol-testing warning apply patch failure

Commit Message

Kalesh A P June 9, 2021, 2:45 a.m. UTC
  From: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>

Add support to fetch the SFP EEPROM settings from the firmware.
For SFP+ modules we will display 0xA0 page for status and 0xA2 page
for other information. For QSFP modules we will show the 0xA0 page.

Also identify the module types for QSFP28, QSFP, QSFP+ apart
from the SFP modules and return an error for 10GBase-T PHY.

Signed-off-by: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>
Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com>
Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
Reviewed-by: Venkat Duvvuru <venkatkumar.duvvuru@broadcom.com>
---
 doc/guides/nics/features/bnxt.ini      |   1 +
 drivers/net/bnxt/bnxt.h                |  15 ++++
 drivers/net/bnxt/bnxt_ethdev.c         | 145 +++++++++++++++++++++++++++++++++
 drivers/net/bnxt/bnxt_hwrm.c           |  36 ++++++++
 drivers/net/bnxt/bnxt_hwrm.h           |   3 +
 drivers/net/bnxt/hsi_struct_def_dpdk.h |  83 +++++++++++++++++++
 6 files changed, 283 insertions(+)
  

Comments

Ajit Khaparde June 16, 2021, 9:51 p.m. UTC | #1
On Tue, Jun 8, 2021 at 7:23 PM Kalesh A P
<kalesh-anakkur.purayil@broadcom.com> wrote:
>
> From: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>
>
> Add support to fetch the SFP EEPROM settings from the firmware.
> For SFP+ modules we will display 0xA0 page for status and 0xA2 page
> for other information. For QSFP modules we will show the 0xA0 page.
>
> Also identify the module types for QSFP28, QSFP, QSFP+ apart
> from the SFP modules and return an error for 10GBase-T PHY.
>
> Signed-off-by: Kalesh AP <kalesh-anakkur.purayil@broadcom.com>
> Reviewed-by: Somnath Kotur <somnath.kotur@broadcom.com>
> Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
> Reviewed-by: Venkat Duvvuru <venkatkumar.duvvuru@broadcom.com>

Patch applied to the for-next-net branch of dpdk-next-net-brcm.

> ---
>  doc/guides/nics/features/bnxt.ini      |   1 +
>  drivers/net/bnxt/bnxt.h                |  15 ++++
>  drivers/net/bnxt/bnxt_ethdev.c         | 145 +++++++++++++++++++++++++++++++++
>  drivers/net/bnxt/bnxt_hwrm.c           |  36 ++++++++
>  drivers/net/bnxt/bnxt_hwrm.h           |   3 +
>  drivers/net/bnxt/hsi_struct_def_dpdk.h |  83 +++++++++++++++++++
>  6 files changed, 283 insertions(+)
>
> diff --git a/doc/guides/nics/features/bnxt.ini b/doc/guides/nics/features/bnxt.ini
> index 291faaa..b6eaca8 100644
> --- a/doc/guides/nics/features/bnxt.ini
> +++ b/doc/guides/nics/features/bnxt.ini
> @@ -42,6 +42,7 @@ Extended stats       = Y
>  Stats per queue      = Y
>  FW version           = Y
>  EEPROM dump          = Y
> +Module EEPROM dump   = Y
>  LED                  = Y
>  Multiprocess aware   = Y
>  FreeBSD              = Y
> diff --git a/drivers/net/bnxt/bnxt.h b/drivers/net/bnxt/bnxt.h
> index e93a7eb..8ec8ddc 100644
> --- a/drivers/net/bnxt/bnxt.h
> +++ b/drivers/net/bnxt/bnxt.h
> @@ -292,6 +292,7 @@ struct bnxt_link_info {
>         uint16_t                auto_pam4_link_speeds;
>         uint16_t                support_pam4_auto_speeds;
>         uint8_t                 req_signal_mode;
> +       uint8_t                 module_status;
>  };
>
>  #define BNXT_COS_QUEUE_COUNT   8
> @@ -965,6 +966,20 @@ struct bnxt_vf_rep_tx_queue {
>         struct bnxt_representor *bp;
>  };
>
> +#define I2C_DEV_ADDR_A0                        0xa0
> +#define I2C_DEV_ADDR_A2                        0xa2
> +#define SFF_DIAG_SUPPORT_OFFSET                0x5c
> +#define SFF_MODULE_ID_SFP              0x3
> +#define SFF_MODULE_ID_QSFP             0xc
> +#define SFF_MODULE_ID_QSFP_PLUS                0xd
> +#define SFF_MODULE_ID_QSFP28           0x11
> +#define SFF8636_FLATMEM_OFFSET         0x2
> +#define SFF8636_FLATMEM_MASK           0x4
> +#define SFF8636_OPT_PAGES_OFFSET       0xc3
> +#define SFF8636_PAGE1_MASK             0x40
> +#define SFF8636_PAGE2_MASK             0x80
> +#define BNXT_MAX_PHY_I2C_RESP_SIZE     64
> +
>  int bnxt_mtu_set_op(struct rte_eth_dev *eth_dev, uint16_t new_mtu);
>  int bnxt_link_update(struct rte_eth_dev *eth_dev, int wait_to_complete,
>                      bool exp_link_status);
> diff --git a/drivers/net/bnxt/bnxt_ethdev.c b/drivers/net/bnxt/bnxt_ethdev.c
> index c9536f7..30aa0ef 100644
> --- a/drivers/net/bnxt/bnxt_ethdev.c
> +++ b/drivers/net/bnxt/bnxt_ethdev.c
> @@ -3851,6 +3851,149 @@ bnxt_set_eeprom_op(struct rte_eth_dev *dev,
>                                      in_eeprom->data, in_eeprom->length);
>  }
>
> +static int bnxt_get_module_info(struct rte_eth_dev *dev,
> +                               struct rte_eth_dev_module_info *modinfo)
> +{
> +       uint8_t module_info[SFF_DIAG_SUPPORT_OFFSET + 1];
> +       struct bnxt *bp = dev->data->dev_private;
> +       int rc;
> +
> +       /* No point in going further if phy status indicates
> +        * module is not inserted or if it is powered down or
> +        * if it is of type 10GBase-T
> +        */
> +       if (bp->link_info->module_status >
> +           HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_WARNINGMSG) {
> +               PMD_DRV_LOG(NOTICE, "Port %u : Module is not inserted or is powered down\n",
> +                           dev->data->port_id);
> +               return -ENOTSUP;
> +       }
> +
> +       /* This feature is not supported in older firmware versions */
> +       if (bp->hwrm_spec_code < 0x10202) {
> +               PMD_DRV_LOG(NOTICE, "Port %u : Feature is not supported in older firmware\n",
> +                           dev->data->port_id);
> +               return -ENOTSUP;
> +       }
> +
> +       rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0,
> +                                                  SFF_DIAG_SUPPORT_OFFSET + 1,
> +                                                  module_info);
> +
> +       if (rc)
> +               return rc;
> +
> +       switch (module_info[0]) {
> +       case SFF_MODULE_ID_SFP:
> +               modinfo->type = RTE_ETH_MODULE_SFF_8472;
> +               modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8472_LEN;
> +               if (module_info[SFF_DIAG_SUPPORT_OFFSET] == 0)
> +                       modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8436_LEN;
> +               break;
> +       case SFF_MODULE_ID_QSFP:
> +       case SFF_MODULE_ID_QSFP_PLUS:
> +               modinfo->type = RTE_ETH_MODULE_SFF_8436;
> +               modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8436_LEN;
> +               break;
> +       case SFF_MODULE_ID_QSFP28:
> +               modinfo->type = RTE_ETH_MODULE_SFF_8636;
> +               modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8636_MAX_LEN;
> +               if (module_info[SFF8636_FLATMEM_OFFSET] & SFF8636_FLATMEM_MASK)
> +                       modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8636_LEN;
> +               break;
> +       default:
> +               PMD_DRV_LOG(NOTICE, "Port %u : Unsupported module\n", dev->data->port_id);
> +               return -ENOTSUP;
> +       }
> +
> +       PMD_DRV_LOG(INFO, "Port %u : modinfo->type = %d modinfo->eeprom_len = %d\n",
> +                   dev->data->port_id, modinfo->type, modinfo->eeprom_len);
> +
> +       return 0;
> +}
> +
> +static int bnxt_get_module_eeprom(struct rte_eth_dev *dev,
> +                                 struct rte_dev_eeprom_info *info)
> +{
> +       uint8_t pg_addr[5] = { I2C_DEV_ADDR_A0, I2C_DEV_ADDR_A0 };
> +       uint32_t offset = info->offset, length = info->length;
> +       uint8_t module_info[SFF_DIAG_SUPPORT_OFFSET + 1];
> +       struct bnxt *bp = dev->data->dev_private;
> +       uint8_t *data = info->data;
> +       uint8_t page = offset >> 7;
> +       uint8_t max_pages = 2;
> +       uint8_t opt_pages;
> +       int rc;
> +
> +       rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0,
> +                                                  SFF_DIAG_SUPPORT_OFFSET + 1,
> +                                                  module_info);
> +       if (rc)
> +               return rc;
> +
> +       switch (module_info[0]) {
> +       case SFF_MODULE_ID_SFP:
> +               module_info[SFF_DIAG_SUPPORT_OFFSET] = 0;
> +               if (module_info[SFF_DIAG_SUPPORT_OFFSET]) {
> +                       pg_addr[2] = I2C_DEV_ADDR_A2;
> +                       pg_addr[3] = I2C_DEV_ADDR_A2;
> +                       max_pages = 4;
> +               }
> +               break;
> +       case SFF_MODULE_ID_QSFP28:
> +               rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0,
> +                                                          SFF8636_OPT_PAGES_OFFSET,
> +                                                          1, &opt_pages);
> +               if (rc)
> +                       return rc;
> +
> +               if (opt_pages & SFF8636_PAGE1_MASK) {
> +                       pg_addr[2] = I2C_DEV_ADDR_A0;
> +                       max_pages = 3;
> +               }
> +               if (opt_pages & SFF8636_PAGE2_MASK) {
> +                       pg_addr[3] = I2C_DEV_ADDR_A0;
> +                       max_pages = 4;
> +               }
> +               if (~module_info[SFF8636_FLATMEM_OFFSET] & SFF8636_FLATMEM_MASK) {
> +                       pg_addr[4] = I2C_DEV_ADDR_A0;
> +                       max_pages = 5;
> +               }
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       memset(data, 0, length);
> +
> +       offset &= 0xff;
> +       while (length && page < max_pages) {
> +               uint8_t raw_page = page ? page - 1 : 0;
> +               uint16_t chunk;
> +
> +               if (pg_addr[page] == I2C_DEV_ADDR_A2)
> +                       raw_page = 0;
> +               else if (page)
> +                       offset |= 0x80;
> +               chunk = RTE_MIN(length, 256 - offset);
> +
> +               if (pg_addr[page]) {
> +                       rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, pg_addr[page],
> +                                                                  raw_page, offset,
> +                                                                  chunk, data);
> +                       if (rc)
> +                               return rc;
> +               }
> +
> +               data += chunk;
> +               length -= chunk;
> +               offset = 0;
> +               page += 1 + (chunk > 128);
> +       }
> +
> +       return length ? -EINVAL : 0;
> +}
> +
>  /*
>   * Initialization
>   */
> @@ -3912,6 +4055,8 @@ static const struct eth_dev_ops bnxt_dev_ops = {
>         .get_eeprom_length    = bnxt_get_eeprom_length_op,
>         .get_eeprom           = bnxt_get_eeprom_op,
>         .set_eeprom           = bnxt_set_eeprom_op,
> +       .get_module_info = bnxt_get_module_info,
> +       .get_module_eeprom = bnxt_get_module_eeprom,
>         .timesync_enable      = bnxt_timesync_enable,
>         .timesync_disable     = bnxt_timesync_disable,
>         .timesync_read_time   = bnxt_timesync_read_time,
> diff --git a/drivers/net/bnxt/bnxt_hwrm.c b/drivers/net/bnxt/bnxt_hwrm.c
> index a65ac6c..31f1d31 100644
> --- a/drivers/net/bnxt/bnxt_hwrm.c
> +++ b/drivers/net/bnxt/bnxt_hwrm.c
> @@ -1502,6 +1502,7 @@ static int bnxt_hwrm_port_phy_qcfg(struct bnxt *bp,
>                         rte_le_to_cpu_16(resp->support_pam4_speeds);
>         link_info->auto_pam4_link_speeds =
>                         rte_le_to_cpu_16(resp->auto_pam4_link_speed_mask);
> +       link_info->module_status = resp->module_status;
>         HWRM_UNLOCK();
>
>         PMD_DRV_LOG(DEBUG, "Link Speed:%d,Auto:%d:%x:%x,Support:%x,Force:%x\n",
> @@ -6157,3 +6158,38 @@ int bnxt_hwrm_poll_ver_get(struct bnxt *bp)
>
>         return rc;
>  }
> +
> +int bnxt_hwrm_read_sfp_module_eeprom_info(struct bnxt *bp, uint16_t i2c_addr,
> +                                         uint16_t page_number, uint16_t start_addr,
> +                                         uint16_t data_length, uint8_t *buf)
> +{
> +       struct hwrm_port_phy_i2c_read_output *resp = bp->hwrm_cmd_resp_addr;
> +       struct hwrm_port_phy_i2c_read_input req = {0};
> +       uint32_t enables = HWRM_PORT_PHY_I2C_READ_INPUT_ENABLES_PAGE_OFFSET;
> +       int rc, byte_offset = 0;
> +
> +       do {
> +               uint16_t xfer_size;
> +
> +               HWRM_PREP(&req, HWRM_PORT_PHY_I2C_READ, BNXT_USE_CHIMP_MB);
> +               req.i2c_slave_addr = i2c_addr;
> +               req.page_number = rte_cpu_to_le_16(page_number);
> +               req.port_id = rte_cpu_to_le_16(bp->pf->port_id);
> +
> +               xfer_size = RTE_MIN(data_length, BNXT_MAX_PHY_I2C_RESP_SIZE);
> +               req.page_offset = rte_cpu_to_le_16(start_addr + byte_offset);
> +               req.data_length = xfer_size;
> +               req.enables = rte_cpu_to_le_32(start_addr + byte_offset ? enables : 0);
> +               rc = bnxt_hwrm_send_message(bp, &req, sizeof(req), BNXT_USE_CHIMP_MB);
> +               HWRM_CHECK_RESULT();
> +
> +               memcpy(buf + byte_offset, resp->data, xfer_size);
> +
> +               data_length -= xfer_size;
> +               byte_offset += xfer_size;
> +
> +               HWRM_UNLOCK();
> +       } while (data_length > 0);
> +
> +       return rc;
> +}
> diff --git a/drivers/net/bnxt/bnxt_hwrm.h b/drivers/net/bnxt/bnxt_hwrm.h
> index b60aa0c..057f7f9 100644
> --- a/drivers/net/bnxt/bnxt_hwrm.h
> +++ b/drivers/net/bnxt/bnxt_hwrm.h
> @@ -301,4 +301,7 @@ int bnxt_hwrm_poll_ver_get(struct bnxt *bp);
>  int bnxt_hwrm_rx_ring_reset(struct bnxt *bp, int queue_index);
>  int bnxt_hwrm_ring_stats(struct bnxt *bp, uint32_t cid, int idx,
>                          struct bnxt_ring_stats *stats, bool rx);
> +int bnxt_hwrm_read_sfp_module_eeprom_info(struct bnxt *bp, uint16_t i2c_addr,
> +                                         uint16_t page_number, uint16_t start_addr,
> +                                         uint16_t data_length, uint8_t *buf);
>  #endif
> diff --git a/drivers/net/bnxt/hsi_struct_def_dpdk.h b/drivers/net/bnxt/hsi_struct_def_dpdk.h
> index aea9305..7428c9c 100644
> --- a/drivers/net/bnxt/hsi_struct_def_dpdk.h
> +++ b/drivers/net/bnxt/hsi_struct_def_dpdk.h
> @@ -48625,4 +48625,87 @@ struct hcomm_status {
>  } __rte_packed;
>  /* This is the GRC offset where the hcomm_status struct resides. */
>  #define HCOMM_STATUS_STRUCT_LOC                0x31001F0UL
> +
> +/**************************
> + * hwrm_port_phy_i2c_read *
> + **************************/
> +
> +
> +/* hwrm_port_phy_i2c_read_input (size:320b/40B) */
> +struct hwrm_port_phy_i2c_read_input {
> +       /* The HWRM command request type. */
> +       uint16_t        req_type;
> +       /*
> +        * The completion ring to send the completion event on. This should
> +        * be the NQ ID returned from the `nq_alloc` HWRM command.
> +        */
> +       uint16_t        cmpl_ring;
> +       /*
> +        * The sequence ID is used by the driver for tracking multiple
> +        * commands. This ID is treated as opaque data by the firmware and
> +        * the value is returned in the `hwrm_resp_hdr` upon completion.
> +        */
> +       uint16_t        seq_id;
> +       /*
> +        * The target ID of the command:
> +        * * 0x0-0xFFF8 - The function ID
> +        * * 0xFFF8-0xFFFC, 0xFFFE - Reserved for internal processors
> +        * * 0xFFFD - Reserved for user-space HWRM interface
> +        * * 0xFFFF - HWRM
> +        */
> +       uint16_t        target_id;
> +       /*
> +        * A physical address pointer pointing to a host buffer that the
> +        * command's response data will be written. This can be either a host
> +        * physical address (HPA) or a guest physical address (GPA) and must
> +        * point to a physically contiguous block of memory.
> +        */
> +       uint64_t        resp_addr;
> +       uint32_t        flags;
> +       uint32_t        enables;
> +       /*
> +        * This bit must be '1' for the page_offset field to be
> +        * configured.
> +        */
> +       #define HWRM_PORT_PHY_I2C_READ_INPUT_ENABLES_PAGE_OFFSET     0x1UL
> +       /* Port ID of port. */
> +       uint16_t        port_id;
> +       /* 8-bit I2C slave address. */
> +       uint8_t i2c_slave_addr;
> +       uint8_t unused_0;
> +       /* The page number that is being accessed over I2C. */
> +       uint16_t        page_number;
> +       /* Offset within the page that is being accessed over I2C. */
> +       uint16_t        page_offset;
> +       /*
> +        * Length of data to read, in bytes starting at the offset
> +        * specified above. If the offset is not specified, then
> +        * the data shall be read from the beginning of the page.
> +        */
> +       uint8_t data_length;
> +       uint8_t unused_1[7];
> +} __rte_packed;
> +
> +/* hwrm_port_phy_i2c_read_output (size:640b/80B) */
> +struct hwrm_port_phy_i2c_read_output {
> +       /* The specific error status for the command. */
> +       uint16_t        error_code;
> +       /* The HWRM command request type. */
> +       uint16_t        req_type;
> +       /* The sequence ID from the original command. */
> +       uint16_t        seq_id;
> +       /* The length of the response data in number of bytes. */
> +       uint16_t        resp_len;
> +       /* Up to 64B of data. */
> +       uint32_t        data[16];
> +       uint8_t unused_0[7];
> +       /*
> +        * This field is used in Output records to indicate that the output
> +        * is completely written to RAM.  This field should be read as '1'
> +        * to indicate that the output has been completely written.
> +        * When writing a command completion or response to an internal processor,
> +        * the order of writes has to be such that this field is written last.
> +        */
> +       uint8_t valid;
> +} __rte_packed;
>  #endif /* _HSI_STRUCT_DEF_DPDK_H_ */
> --
> 2.10.1
>
  

Patch

diff --git a/doc/guides/nics/features/bnxt.ini b/doc/guides/nics/features/bnxt.ini
index 291faaa..b6eaca8 100644
--- a/doc/guides/nics/features/bnxt.ini
+++ b/doc/guides/nics/features/bnxt.ini
@@ -42,6 +42,7 @@  Extended stats       = Y
 Stats per queue      = Y
 FW version           = Y
 EEPROM dump          = Y
+Module EEPROM dump   = Y
 LED                  = Y
 Multiprocess aware   = Y
 FreeBSD              = Y
diff --git a/drivers/net/bnxt/bnxt.h b/drivers/net/bnxt/bnxt.h
index e93a7eb..8ec8ddc 100644
--- a/drivers/net/bnxt/bnxt.h
+++ b/drivers/net/bnxt/bnxt.h
@@ -292,6 +292,7 @@  struct bnxt_link_info {
 	uint16_t		auto_pam4_link_speeds;
 	uint16_t		support_pam4_auto_speeds;
 	uint8_t			req_signal_mode;
+	uint8_t			module_status;
 };
 
 #define BNXT_COS_QUEUE_COUNT	8
@@ -965,6 +966,20 @@  struct bnxt_vf_rep_tx_queue {
 	struct bnxt_representor *bp;
 };
 
+#define I2C_DEV_ADDR_A0			0xa0
+#define I2C_DEV_ADDR_A2			0xa2
+#define SFF_DIAG_SUPPORT_OFFSET		0x5c
+#define SFF_MODULE_ID_SFP		0x3
+#define SFF_MODULE_ID_QSFP		0xc
+#define SFF_MODULE_ID_QSFP_PLUS		0xd
+#define SFF_MODULE_ID_QSFP28		0x11
+#define SFF8636_FLATMEM_OFFSET		0x2
+#define SFF8636_FLATMEM_MASK		0x4
+#define SFF8636_OPT_PAGES_OFFSET	0xc3
+#define SFF8636_PAGE1_MASK		0x40
+#define SFF8636_PAGE2_MASK		0x80
+#define BNXT_MAX_PHY_I2C_RESP_SIZE	64
+
 int bnxt_mtu_set_op(struct rte_eth_dev *eth_dev, uint16_t new_mtu);
 int bnxt_link_update(struct rte_eth_dev *eth_dev, int wait_to_complete,
 		     bool exp_link_status);
diff --git a/drivers/net/bnxt/bnxt_ethdev.c b/drivers/net/bnxt/bnxt_ethdev.c
index c9536f7..30aa0ef 100644
--- a/drivers/net/bnxt/bnxt_ethdev.c
+++ b/drivers/net/bnxt/bnxt_ethdev.c
@@ -3851,6 +3851,149 @@  bnxt_set_eeprom_op(struct rte_eth_dev *dev,
 				     in_eeprom->data, in_eeprom->length);
 }
 
+static int bnxt_get_module_info(struct rte_eth_dev *dev,
+				struct rte_eth_dev_module_info *modinfo)
+{
+	uint8_t module_info[SFF_DIAG_SUPPORT_OFFSET + 1];
+	struct bnxt *bp = dev->data->dev_private;
+	int rc;
+
+	/* No point in going further if phy status indicates
+	 * module is not inserted or if it is powered down or
+	 * if it is of type 10GBase-T
+	 */
+	if (bp->link_info->module_status >
+	    HWRM_PORT_PHY_QCFG_OUTPUT_MODULE_STATUS_WARNINGMSG) {
+		PMD_DRV_LOG(NOTICE, "Port %u : Module is not inserted or is powered down\n",
+			    dev->data->port_id);
+		return -ENOTSUP;
+	}
+
+	/* This feature is not supported in older firmware versions */
+	if (bp->hwrm_spec_code < 0x10202) {
+		PMD_DRV_LOG(NOTICE, "Port %u : Feature is not supported in older firmware\n",
+			    dev->data->port_id);
+		return -ENOTSUP;
+	}
+
+	rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0,
+						   SFF_DIAG_SUPPORT_OFFSET + 1,
+						   module_info);
+
+	if (rc)
+		return rc;
+
+	switch (module_info[0]) {
+	case SFF_MODULE_ID_SFP:
+		modinfo->type = RTE_ETH_MODULE_SFF_8472;
+		modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8472_LEN;
+		if (module_info[SFF_DIAG_SUPPORT_OFFSET] == 0)
+			modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8436_LEN;
+		break;
+	case SFF_MODULE_ID_QSFP:
+	case SFF_MODULE_ID_QSFP_PLUS:
+		modinfo->type = RTE_ETH_MODULE_SFF_8436;
+		modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8436_LEN;
+		break;
+	case SFF_MODULE_ID_QSFP28:
+		modinfo->type = RTE_ETH_MODULE_SFF_8636;
+		modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8636_MAX_LEN;
+		if (module_info[SFF8636_FLATMEM_OFFSET] & SFF8636_FLATMEM_MASK)
+			modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8636_LEN;
+		break;
+	default:
+		PMD_DRV_LOG(NOTICE, "Port %u : Unsupported module\n", dev->data->port_id);
+		return -ENOTSUP;
+	}
+
+	PMD_DRV_LOG(INFO, "Port %u : modinfo->type = %d modinfo->eeprom_len = %d\n",
+		    dev->data->port_id, modinfo->type, modinfo->eeprom_len);
+
+	return 0;
+}
+
+static int bnxt_get_module_eeprom(struct rte_eth_dev *dev,
+				  struct rte_dev_eeprom_info *info)
+{
+	uint8_t pg_addr[5] = { I2C_DEV_ADDR_A0, I2C_DEV_ADDR_A0 };
+	uint32_t offset = info->offset, length = info->length;
+	uint8_t module_info[SFF_DIAG_SUPPORT_OFFSET + 1];
+	struct bnxt *bp = dev->data->dev_private;
+	uint8_t *data = info->data;
+	uint8_t page = offset >> 7;
+	uint8_t max_pages = 2;
+	uint8_t opt_pages;
+	int rc;
+
+	rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0, 0,
+						   SFF_DIAG_SUPPORT_OFFSET + 1,
+						   module_info);
+	if (rc)
+		return rc;
+
+	switch (module_info[0]) {
+	case SFF_MODULE_ID_SFP:
+		module_info[SFF_DIAG_SUPPORT_OFFSET] = 0;
+		if (module_info[SFF_DIAG_SUPPORT_OFFSET]) {
+			pg_addr[2] = I2C_DEV_ADDR_A2;
+			pg_addr[3] = I2C_DEV_ADDR_A2;
+			max_pages = 4;
+		}
+		break;
+	case SFF_MODULE_ID_QSFP28:
+		rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0,
+							   SFF8636_OPT_PAGES_OFFSET,
+							   1, &opt_pages);
+		if (rc)
+			return rc;
+
+		if (opt_pages & SFF8636_PAGE1_MASK) {
+			pg_addr[2] = I2C_DEV_ADDR_A0;
+			max_pages = 3;
+		}
+		if (opt_pages & SFF8636_PAGE2_MASK) {
+			pg_addr[3] = I2C_DEV_ADDR_A0;
+			max_pages = 4;
+		}
+		if (~module_info[SFF8636_FLATMEM_OFFSET] & SFF8636_FLATMEM_MASK) {
+			pg_addr[4] = I2C_DEV_ADDR_A0;
+			max_pages = 5;
+		}
+		break;
+	default:
+		break;
+	}
+
+	memset(data, 0, length);
+
+	offset &= 0xff;
+	while (length && page < max_pages) {
+		uint8_t raw_page = page ? page - 1 : 0;
+		uint16_t chunk;
+
+		if (pg_addr[page] == I2C_DEV_ADDR_A2)
+			raw_page = 0;
+		else if (page)
+			offset |= 0x80;
+		chunk = RTE_MIN(length, 256 - offset);
+
+		if (pg_addr[page]) {
+			rc = bnxt_hwrm_read_sfp_module_eeprom_info(bp, pg_addr[page],
+								   raw_page, offset,
+								   chunk, data);
+			if (rc)
+				return rc;
+		}
+
+		data += chunk;
+		length -= chunk;
+		offset = 0;
+		page += 1 + (chunk > 128);
+	}
+
+	return length ? -EINVAL : 0;
+}
+
 /*
  * Initialization
  */
@@ -3912,6 +4055,8 @@  static const struct eth_dev_ops bnxt_dev_ops = {
 	.get_eeprom_length    = bnxt_get_eeprom_length_op,
 	.get_eeprom           = bnxt_get_eeprom_op,
 	.set_eeprom           = bnxt_set_eeprom_op,
+	.get_module_info = bnxt_get_module_info,
+	.get_module_eeprom = bnxt_get_module_eeprom,
 	.timesync_enable      = bnxt_timesync_enable,
 	.timesync_disable     = bnxt_timesync_disable,
 	.timesync_read_time   = bnxt_timesync_read_time,
diff --git a/drivers/net/bnxt/bnxt_hwrm.c b/drivers/net/bnxt/bnxt_hwrm.c
index a65ac6c..31f1d31 100644
--- a/drivers/net/bnxt/bnxt_hwrm.c
+++ b/drivers/net/bnxt/bnxt_hwrm.c
@@ -1502,6 +1502,7 @@  static int bnxt_hwrm_port_phy_qcfg(struct bnxt *bp,
 			rte_le_to_cpu_16(resp->support_pam4_speeds);
 	link_info->auto_pam4_link_speeds =
 			rte_le_to_cpu_16(resp->auto_pam4_link_speed_mask);
+	link_info->module_status = resp->module_status;
 	HWRM_UNLOCK();
 
 	PMD_DRV_LOG(DEBUG, "Link Speed:%d,Auto:%d:%x:%x,Support:%x,Force:%x\n",
@@ -6157,3 +6158,38 @@  int bnxt_hwrm_poll_ver_get(struct bnxt *bp)
 
 	return rc;
 }
+
+int bnxt_hwrm_read_sfp_module_eeprom_info(struct bnxt *bp, uint16_t i2c_addr,
+					  uint16_t page_number, uint16_t start_addr,
+					  uint16_t data_length, uint8_t *buf)
+{
+	struct hwrm_port_phy_i2c_read_output *resp = bp->hwrm_cmd_resp_addr;
+	struct hwrm_port_phy_i2c_read_input req = {0};
+	uint32_t enables = HWRM_PORT_PHY_I2C_READ_INPUT_ENABLES_PAGE_OFFSET;
+	int rc, byte_offset = 0;
+
+	do {
+		uint16_t xfer_size;
+
+		HWRM_PREP(&req, HWRM_PORT_PHY_I2C_READ, BNXT_USE_CHIMP_MB);
+		req.i2c_slave_addr = i2c_addr;
+		req.page_number = rte_cpu_to_le_16(page_number);
+		req.port_id = rte_cpu_to_le_16(bp->pf->port_id);
+
+		xfer_size = RTE_MIN(data_length, BNXT_MAX_PHY_I2C_RESP_SIZE);
+		req.page_offset = rte_cpu_to_le_16(start_addr + byte_offset);
+		req.data_length = xfer_size;
+		req.enables = rte_cpu_to_le_32(start_addr + byte_offset ? enables : 0);
+		rc = bnxt_hwrm_send_message(bp, &req, sizeof(req), BNXT_USE_CHIMP_MB);
+		HWRM_CHECK_RESULT();
+
+		memcpy(buf + byte_offset, resp->data, xfer_size);
+
+		data_length -= xfer_size;
+		byte_offset += xfer_size;
+
+		HWRM_UNLOCK();
+	} while (data_length > 0);
+
+	return rc;
+}
diff --git a/drivers/net/bnxt/bnxt_hwrm.h b/drivers/net/bnxt/bnxt_hwrm.h
index b60aa0c..057f7f9 100644
--- a/drivers/net/bnxt/bnxt_hwrm.h
+++ b/drivers/net/bnxt/bnxt_hwrm.h
@@ -301,4 +301,7 @@  int bnxt_hwrm_poll_ver_get(struct bnxt *bp);
 int bnxt_hwrm_rx_ring_reset(struct bnxt *bp, int queue_index);
 int bnxt_hwrm_ring_stats(struct bnxt *bp, uint32_t cid, int idx,
 			 struct bnxt_ring_stats *stats, bool rx);
+int bnxt_hwrm_read_sfp_module_eeprom_info(struct bnxt *bp, uint16_t i2c_addr,
+					  uint16_t page_number, uint16_t start_addr,
+					  uint16_t data_length, uint8_t *buf);
 #endif
diff --git a/drivers/net/bnxt/hsi_struct_def_dpdk.h b/drivers/net/bnxt/hsi_struct_def_dpdk.h
index aea9305..7428c9c 100644
--- a/drivers/net/bnxt/hsi_struct_def_dpdk.h
+++ b/drivers/net/bnxt/hsi_struct_def_dpdk.h
@@ -48625,4 +48625,87 @@  struct hcomm_status {
 } __rte_packed;
 /* This is the GRC offset where the hcomm_status struct resides. */
 #define HCOMM_STATUS_STRUCT_LOC		0x31001F0UL
+
+/**************************
+ * hwrm_port_phy_i2c_read *
+ **************************/
+
+
+/* hwrm_port_phy_i2c_read_input (size:320b/40B) */
+struct hwrm_port_phy_i2c_read_input {
+	/* The HWRM command request type. */
+	uint16_t	req_type;
+	/*
+	 * The completion ring to send the completion event on. This should
+	 * be the NQ ID returned from the `nq_alloc` HWRM command.
+	 */
+	uint16_t	cmpl_ring;
+	/*
+	 * The sequence ID is used by the driver for tracking multiple
+	 * commands. This ID is treated as opaque data by the firmware and
+	 * the value is returned in the `hwrm_resp_hdr` upon completion.
+	 */
+	uint16_t	seq_id;
+	/*
+	 * The target ID of the command:
+	 * * 0x0-0xFFF8 - The function ID
+	 * * 0xFFF8-0xFFFC, 0xFFFE - Reserved for internal processors
+	 * * 0xFFFD - Reserved for user-space HWRM interface
+	 * * 0xFFFF - HWRM
+	 */
+	uint16_t	target_id;
+	/*
+	 * A physical address pointer pointing to a host buffer that the
+	 * command's response data will be written. This can be either a host
+	 * physical address (HPA) or a guest physical address (GPA) and must
+	 * point to a physically contiguous block of memory.
+	 */
+	uint64_t	resp_addr;
+	uint32_t	flags;
+	uint32_t	enables;
+	/*
+	 * This bit must be '1' for the page_offset field to be
+	 * configured.
+	 */
+	#define HWRM_PORT_PHY_I2C_READ_INPUT_ENABLES_PAGE_OFFSET     0x1UL
+	/* Port ID of port. */
+	uint16_t	port_id;
+	/* 8-bit I2C slave address. */
+	uint8_t	i2c_slave_addr;
+	uint8_t	unused_0;
+	/* The page number that is being accessed over I2C. */
+	uint16_t	page_number;
+	/* Offset within the page that is being accessed over I2C. */
+	uint16_t	page_offset;
+	/*
+	 * Length of data to read, in bytes starting at the offset
+	 * specified above. If the offset is not specified, then
+	 * the data shall be read from the beginning of the page.
+	 */
+	uint8_t	data_length;
+	uint8_t	unused_1[7];
+} __rte_packed;
+
+/* hwrm_port_phy_i2c_read_output (size:640b/80B) */
+struct hwrm_port_phy_i2c_read_output {
+	/* The specific error status for the command. */
+	uint16_t	error_code;
+	/* The HWRM command request type. */
+	uint16_t	req_type;
+	/* The sequence ID from the original command. */
+	uint16_t	seq_id;
+	/* The length of the response data in number of bytes. */
+	uint16_t	resp_len;
+	/* Up to 64B of data. */
+	uint32_t	data[16];
+	uint8_t	unused_0[7];
+	/*
+	 * This field is used in Output records to indicate that the output
+	 * is completely written to RAM.  This field should be read as '1'
+	 * to indicate that the output has been completely written.
+	 * When writing a command completion or response to an internal processor,
+	 * the order of writes has to be such that this field is written last.
+	 */
+	uint8_t	valid;
+} __rte_packed;
 #endif /* _HSI_STRUCT_DEF_DPDK_H_ */