Add support for querying and reading the EEPROM of SFP/QSFP modules. Signed-off-by: Rong Qian <qi...@yunsilicon.com> Signed-off-by: Renyong Wan <wa...@yunsilicon.com> --- doc/guides/nics/features/xsc.ini | 1 + drivers/net/xsc/xsc_cmd.h | 15 +++ drivers/net/xsc/xsc_dev.c | 185 +++++++++++++++++++++++++++++++ drivers/net/xsc/xsc_dev.h | 63 +++++++++++ drivers/net/xsc/xsc_ethdev.c | 107 ++++++++++++++++++ 5 files changed, 371 insertions(+)
diff --git a/doc/guides/nics/features/xsc.ini b/doc/guides/nics/features/xsc.ini index 3b02289764..46c56fc921 100644 --- a/doc/guides/nics/features/xsc.ini +++ b/doc/guides/nics/features/xsc.ini @@ -16,6 +16,7 @@ Inner L4 checksum = Y Basic stats = Y Stats per queue = Y FW version = Y +Module EEPROM dump = Y Linux = Y ARMv8 = Y x86-64 = Y diff --git a/drivers/net/xsc/xsc_cmd.h b/drivers/net/xsc/xsc_cmd.h index 433dcd0afa..82244d4011 100644 --- a/drivers/net/xsc/xsc_cmd.h +++ b/drivers/net/xsc/xsc_cmd.h @@ -23,6 +23,7 @@ enum xsc_cmd_opcode { XSC_CMD_OP_RTR2RTS_QP = 0x504, XSC_CMD_OP_QP_2RST = 0x50A, XSC_CMD_OP_CREATE_MULTI_QP = 0x515, + XSC_CMD_OP_ACCESS_REG = 0x805, XSC_CMD_OP_MODIFY_NIC_HCA = 0x812, XSC_CMD_OP_MODIFY_RAW_QP = 0x81f, XSC_CMD_OP_EXEC_NP = 0x900, @@ -384,4 +385,18 @@ struct xsc_cmd_modify_nic_hca_mbox_out { uint8_t rsvd[4]; }; +struct xsc_cmd_access_reg_mbox_in { + struct xsc_cmd_inbox_hdr hdr; + uint8_t rsvd0[2]; + rte_be16_t register_id; + rte_be32_t arg; + rte_be32_t data[]; +}; + +struct xsc_cmd_access_reg_mbox_out { + struct xsc_cmd_outbox_hdr hdr; + uint8_t rsvd[8]; + rte_be32_t data[]; +}; + #endif /* _XSC_CMD_H_ */ diff --git a/drivers/net/xsc/xsc_dev.c b/drivers/net/xsc/xsc_dev.c index 66f98d2327..2c55306d35 100644 --- a/drivers/net/xsc/xsc_dev.c +++ b/drivers/net/xsc/xsc_dev.c @@ -411,3 +411,188 @@ xsc_dev_fw_version_get(struct xsc_dev *xdev, char *fw_version, size_t fw_size) return 0; } + +static int +xsc_dev_access_reg(struct xsc_dev *xdev, void *data_in, int size_in, + void *data_out, int size_out, uint16_t reg_num) +{ + struct xsc_cmd_access_reg_mbox_in *in; + struct xsc_cmd_access_reg_mbox_out *out; + int ret = -1; + + in = malloc(sizeof(*in) + size_in); + if (in == NULL) { + rte_errno = ENOMEM; + PMD_DRV_LOG(ERR, "Failed to malloc access reg mbox in memory"); + return -rte_errno; + } + memset(in, 0, sizeof(*in) + size_in); + + out = malloc(sizeof(*out) + size_out); + if (out == NULL) { + rte_errno = ENOMEM; + PMD_DRV_LOG(ERR, "Failed to malloc access reg mbox out memory"); + goto alloc_out_fail; + } + memset(out, 0, sizeof(*out) + size_out); + + memcpy(in->data, data_in, size_in); + in->hdr.opcode = rte_cpu_to_be_16(XSC_CMD_OP_ACCESS_REG); + in->arg = 0; + in->register_id = rte_cpu_to_be_16(reg_num); + + ret = xsc_dev_mailbox_exec(xdev, in, sizeof(*in) + size_in, out, + sizeof(*out) + size_out); + if (ret != 0 || out->hdr.status != 0) { + rte_errno = ENOEXEC; + PMD_DRV_LOG(ERR, "Failed to access reg"); + goto exit; + } + + memcpy(data_out, out->data, size_out); + +exit: + free(out); + +alloc_out_fail: + free(in); + return ret; +} + +static int +xsc_dev_query_mcia(struct xsc_dev *xdev, + struct xsc_module_eeprom_query_params *params, + uint8_t *data) +{ + struct xsc_dev_reg_mcia in = { }; + struct xsc_dev_reg_mcia out = { }; + int ret; + void *ptr; + uint16_t size; + + size = RTE_MIN(params->size, XSC_EEPROM_MAX_BYTES); + in.i2c_device_address = params->i2c_address; + in.module = params->module_number & 0x000000FF; + in.device_address = params->offset; + in.page_number = params->page; + in.size = size; + + ret = xsc_dev_access_reg(xdev, &in, sizeof(in), &out, sizeof(out), XSC_REG_MCIA); + if (ret != 0) + return ret; + + ptr = out.dword_0; + memcpy(data, ptr, size); + + return size; +} + +static void +xsc_dev_sfp_eeprom_params_set(uint16_t *i2c_addr, uint32_t *page_num, uint16_t *offset) +{ + *i2c_addr = XSC_I2C_ADDR_LOW; + *page_num = 0; + + if (*offset < XSC_EEPROM_PAGE_LENGTH) + return; + + *i2c_addr = XSC_I2C_ADDR_HIGH; + *offset -= XSC_EEPROM_PAGE_LENGTH; +} + +static int +xsc_dev_qsfp_eeprom_page(uint16_t offset) +{ + if (offset < XSC_EEPROM_PAGE_LENGTH) + /* Addresses between 0-255 - page 00 */ + return 0; + + /* + * Addresses between 256 - 639 belongs to pages 01, 02 and 03 + * For example, offset = 400 belongs to page 02: + * 1 + ((400 - 256)/128) = 2 + */ + return 1 + ((offset - XSC_EEPROM_PAGE_LENGTH) / XSC_EEPROM_HIGH_PAGE_LENGTH); +} + +static int +xsc_dev_qsfp_eeprom_high_page_offset(int page_num) +{ + /* Page 0 always start from low page */ + if (!page_num) + return 0; + + /* High page */ + return page_num * XSC_EEPROM_HIGH_PAGE_LENGTH; +} + +static void +xsc_dev_qsfp_eeprom_params_set(uint16_t *i2c_addr, uint32_t *page_num, uint16_t *offset) +{ + *i2c_addr = XSC_I2C_ADDR_LOW; + *page_num = xsc_dev_qsfp_eeprom_page(*offset); + *offset -= xsc_dev_qsfp_eeprom_high_page_offset(*page_num); +} + +static int +xsc_dev_query_module_id(struct xsc_dev *xdev, uint32_t module_num, uint8_t *module_id) +{ + struct xsc_dev_reg_mcia in = { 0 }; + struct xsc_dev_reg_mcia out = { 0 }; + int ret; + uint8_t *ptr; + + in.i2c_device_address = XSC_I2C_ADDR_LOW; + in.module = module_num & 0x000000FF; + in.device_address = 0; + in.page_number = 0; + in.size = 1; + + ret = xsc_dev_access_reg(xdev, &in, sizeof(in), &out, sizeof(out), XSC_REG_MCIA); + if (ret != 0) + return ret; + + ptr = out.dword_0; + *module_id = ptr[0]; + return 0; +} + +int +xsc_dev_query_module_eeprom(struct xsc_dev *xdev, uint16_t offset, + uint16_t size, uint8_t *data) +{ + struct xsc_module_eeprom_query_params query = { 0 }; + uint8_t module_id; + int ret; + + query.module_number = xdev->hwinfo.mac_phy_port; + ret = xsc_dev_query_module_id(xdev, query.module_number, &module_id); + if (ret != 0) + return ret; + + switch (module_id) { + case XSC_MODULE_ID_SFP: + xsc_dev_sfp_eeprom_params_set(&query.i2c_address, &query.page, &offset); + break; + case XSC_MODULE_ID_QSFP: + case XSC_MODULE_ID_QSFP_PLUS: + case XSC_MODULE_ID_QSFP28: + case XSC_MODULE_ID_QSFP_DD: + case XSC_MODULE_ID_DSFP: + case XSC_MODULE_ID_QSFP_PLUS_CMIS: + xsc_dev_qsfp_eeprom_params_set(&query.i2c_address, &query.page, &offset); + break; + default: + PMD_DRV_LOG(ERR, "Module ID not recognized: 0x%x", module_id); + return -EINVAL; + } + + if (offset + size > XSC_EEPROM_PAGE_LENGTH) + /* Cross pages read, read until offset 256 in low page */ + size = XSC_EEPROM_PAGE_LENGTH - offset; + + query.size = size; + query.offset = offset; + + return xsc_dev_query_mcia(xdev, &query, data); +} diff --git a/drivers/net/xsc/xsc_dev.h b/drivers/net/xsc/xsc_dev.h index 274d6c6a2e..5cf19aaf3d 100644 --- a/drivers/net/xsc/xsc_dev.h +++ b/drivers/net/xsc/xsc_dev.h @@ -28,6 +28,12 @@ #define XSC_DEV_REPR_ID_INVALID 0x7FFFFFFF #define XSC_FW_VERS_LEN 64 +#define XSC_DWORD_LEN 0x20 +#define XSC_I2C_ADDR_LOW 0x50 +#define XSC_I2C_ADDR_HIGH 0x51 +#define XSC_EEPROM_PAGE_LENGTH 256 +#define XSC_EEPROM_HIGH_PAGE_LENGTH 128 +#define XSC_EEPROM_MAX_BYTES 32 enum xsc_queue_type { XSC_QUEUE_TYPE_RDMA_RC = 0, @@ -41,6 +47,31 @@ enum xsc_queue_type { XSC_QUEUE_TYPE_INVALID = 0xFF, }; +enum xsc_reg_type { + XSC_REG_PMLP = 0x0, + XSC_REG_PCAP = 0x5001, + XSC_REG_PMTU = 0x5003, + XSC_REG_PTYS = 0x5004, + XSC_REG_PAOS = 0x5006, + XSC_REG_PMAOS = 0x5012, + XSC_REG_PUDE = 0x5009, + XSC_REG_PMPE = 0x5010, + XSC_REG_PELC = 0x500e, + XSC_REG_NODE_DESC = 0x6001, + XSC_REG_HOST_ENDIANNESS = 0x7004, + XSC_REG_MCIA = 0x9014, +}; + +enum xsc_module_id { + XSC_MODULE_ID_SFP = 0x3, + XSC_MODULE_ID_QSFP = 0xC, + XSC_MODULE_ID_QSFP_PLUS = 0xD, + XSC_MODULE_ID_QSFP28 = 0x11, + XSC_MODULE_ID_QSFP_DD = 0x18, + XSC_MODULE_ID_DSFP = 0x1B, + XSC_MODULE_ID_QSFP_PLUS_CMIS = 0x1E, +}; + struct xsc_hwinfo { uint32_t pcie_no; /* pcie number , 0 or 1 */ uint32_t func_id; /* pf glb func id */ @@ -132,6 +163,36 @@ struct xsc_dev { int ctrl_fd; }; +struct xsc_module_eeprom_query_params { + uint16_t size; + uint16_t offset; + uint16_t i2c_address; + uint32_t page; + uint32_t bank; + uint32_t module_number; +}; + +struct xsc_dev_reg_mcia { + uint8_t module; + uint8_t status; + uint8_t i2c_device_address; + uint8_t page_number; + uint8_t device_address; + uint8_t size; + uint8_t dword_0[XSC_DWORD_LEN]; + uint8_t dword_1[XSC_DWORD_LEN]; + uint8_t dword_2[XSC_DWORD_LEN]; + uint8_t dword_3[XSC_DWORD_LEN]; + uint8_t dword_4[XSC_DWORD_LEN]; + uint8_t dword_5[XSC_DWORD_LEN]; + uint8_t dword_6[XSC_DWORD_LEN]; + uint8_t dword_7[XSC_DWORD_LEN]; + uint8_t dword_8[XSC_DWORD_LEN]; + uint8_t dword_9[XSC_DWORD_LEN]; + uint8_t dword_10[XSC_DWORD_LEN]; + uint8_t dword_11[XSC_DWORD_LEN]; +}; + struct xsc_dev_ops { TAILQ_ENTRY(xsc_dev_ops) entry; enum rte_pci_kernel_driver kdrv; @@ -184,5 +245,7 @@ int xsc_dev_qp_set_id_get(struct xsc_dev *xdev, int repr_id); int xsc_dev_set_mtu(struct xsc_dev *xdev, uint16_t mtu); int xsc_dev_get_mac(struct xsc_dev *xdev, uint8_t *mac); int xsc_dev_fw_version_get(struct xsc_dev *xdev, char *fw_version, size_t fw_size); +int xsc_dev_query_module_eeprom(struct xsc_dev *xdev, uint16_t offset, + uint16_t size, uint8_t *data); #endif /* _XSC_DEV_H_ */ diff --git a/drivers/net/xsc/xsc_ethdev.c b/drivers/net/xsc/xsc_ethdev.c index 9a70be0543..ffcf04a90c 100644 --- a/drivers/net/xsc/xsc_ethdev.c +++ b/drivers/net/xsc/xsc_ethdev.c @@ -2,6 +2,7 @@ * Copyright 2025 Yunsilicon Technology Co., Ltd. */ +#include <rte_dev_info.h> #include <ethdev_pci.h> #include "xsc_log.h" @@ -681,6 +682,110 @@ xsc_ethdev_fw_version_get(struct rte_eth_dev *dev, char *fw_version, size_t fw_s return xsc_dev_fw_version_get(priv->xdev, fw_version, fw_size); } +static int +xsc_ethdev_get_module_info(struct rte_eth_dev *dev, + struct rte_eth_dev_module_info *modinfo) +{ + int size_read = 0; + uint8_t data[4] = { 0 }; + struct xsc_ethdev_priv *priv = TO_XSC_ETHDEV_PRIV(dev); + + size_read = xsc_dev_query_module_eeprom(priv->xdev, 0, 3, data); + if (size_read < 3) + return -1; + + /* data[0] = identifier byte */ + switch (data[0]) { + case XSC_MODULE_ID_QSFP: + modinfo->type = RTE_ETH_MODULE_SFF_8436; + modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8436_MAX_LEN; + break; + case XSC_MODULE_ID_QSFP_PLUS: + case XSC_MODULE_ID_QSFP28: + /* data[1] = revision id */ + if (data[0] == XSC_MODULE_ID_QSFP28 || data[1] >= 0x3) { + modinfo->type = RTE_ETH_MODULE_SFF_8636; + modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8636_MAX_LEN; + } else { + modinfo->type = RTE_ETH_MODULE_SFF_8436; + modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8436_MAX_LEN; + } + break; + case XSC_MODULE_ID_SFP: + modinfo->type = RTE_ETH_MODULE_SFF_8472; + modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8472_LEN; + break; + case XSC_MODULE_ID_QSFP_DD: + case XSC_MODULE_ID_DSFP: + case XSC_MODULE_ID_QSFP_PLUS_CMIS: + modinfo->type = RTE_ETH_MODULE_SFF_8636; + /* Verify if module EEPROM is a flat memory. In case of flat + * memory only page 00h (0-255 bytes) can be read. Otherwise + * upper pages 01h and 02h can also be read. Upper pages 10h + * and 11h are currently not supported by the driver. + */ + if (data[2] & 0x80) + modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8636_LEN; + else + modinfo->eeprom_len = RTE_ETH_MODULE_SFF_8472_LEN; + break; + default: + PMD_DRV_LOG(ERR, "Cable type 0x%x not recognized", + data[0]); + return -EINVAL; + } + + return 0; +} + +static int +xsc_ethdev_get_module_eeprom(struct rte_eth_dev *dev, + struct rte_dev_eeprom_info *info) +{ + uint32_t i = 0; + uint8_t *data; + int size_read; + uint32_t offset = info->offset; + struct xsc_ethdev_priv *priv = TO_XSC_ETHDEV_PRIV(dev); + + if (info->length == 0) { + PMD_DRV_LOG(ERR, "Failed to get module eeprom, eeprom length is 0"); + rte_errno = EINVAL; + return -rte_errno; + } + + data = malloc(info->length); + if (data == NULL) { + PMD_DRV_LOG(ERR, "Failed to get module eeprom, cannot allocate memory"); + rte_errno = ENOMEM; + return -rte_errno; + } + memset(data, 0, info->length); + + while (i < info->length) { + size_read = xsc_dev_query_module_eeprom(priv->xdev, offset, + info->length - i, data + i); + if (!size_read) + /* Done reading */ + goto exit; + + if (size_read < 0) { + PMD_DRV_LOG(ERR, "Failed to get module eeprom, size read=%d", + size_read); + goto exit; + } + + i += size_read; + offset += size_read; + } + + memcpy(info->data, data, info->length); + +exit: + free(data); + return 0; +} + const struct eth_dev_ops xsc_eth_dev_ops = { .dev_configure = xsc_ethdev_configure, .dev_start = xsc_ethdev_start, @@ -700,6 +805,8 @@ const struct eth_dev_ops xsc_eth_dev_ops = { .rss_hash_update = xsc_ethdev_rss_hash_update, .rss_hash_conf_get = xsc_ethdev_rss_hash_conf_get, .fw_version_get = xsc_ethdev_fw_version_get, + .get_module_info = xsc_ethdev_get_module_info, + .get_module_eeprom = xsc_ethdev_get_module_eeprom, }; static int -- 2.25.1