bnxt_en: Add Support for ETHTOOL_GMODULEINFO and ETHTOOL_GMODULEEEPRO
Add support to fetch the SFP EEPROM settings from the firmware and display it via the ethtool -m command. We support SFP+ and QSFP modules. v2: Fixed a bug in bnxt_get_module_eeprom() found by Ben Hutchings. Signed-off-by: Ajit Khaparde <ajit.khaparde@broadcom.com> Signed-off-by: Michael Chan <michael.chan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
18d6e4e2d8
commit
42ee18fe4c
|
@ -4734,6 +4734,7 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state)
|
|||
link_info->transceiver = resp->xcvr_pkg_type;
|
||||
link_info->phy_addr = resp->eee_config_phy_addr &
|
||||
PORT_PHY_QCFG_RESP_PHY_ADDR_MASK;
|
||||
link_info->module_status = resp->module_status;
|
||||
|
||||
if (bp->flags & BNXT_FLAG_EEE_CAP) {
|
||||
struct ethtool_eee *eee = &bp->eee;
|
||||
|
|
|
@ -831,6 +831,7 @@ struct bnxt_link_info {
|
|||
u16 lp_auto_link_speeds;
|
||||
u16 force_link_speed;
|
||||
u32 preemphasis;
|
||||
u8 module_status;
|
||||
|
||||
/* copy of requested setting from ethtool cmd */
|
||||
u8 autoneg;
|
||||
|
@ -1123,6 +1124,16 @@ static inline void bnxt_disable_poll(struct bnxt_napi *bnapi)
|
|||
|
||||
#endif
|
||||
|
||||
#define I2C_DEV_ADDR_A0 0xa0
|
||||
#define I2C_DEV_ADDR_A2 0xa2
|
||||
#define SFP_EEPROM_SFF_8472_COMP_ADDR 0x5e
|
||||
#define SFP_EEPROM_SFF_8472_COMP_SIZE 1
|
||||
#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 BNXT_MAX_PHY_I2C_RESP_SIZE 64
|
||||
|
||||
void bnxt_set_ring_params(struct bnxt *);
|
||||
void bnxt_hwrm_cmd_hdr_init(struct bnxt *, void *, u16, u16, u16);
|
||||
int _hwrm_send_message(struct bnxt *, void *, u32, int);
|
||||
|
|
|
@ -1498,6 +1498,125 @@ static int bnxt_get_eee(struct net_device *dev, struct ethtool_eee *edata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bnxt_read_sfp_module_eeprom_info(struct bnxt *bp, u16 i2c_addr,
|
||||
u16 page_number, u16 start_addr,
|
||||
u16 data_length, u8 *buf)
|
||||
{
|
||||
struct hwrm_port_phy_i2c_read_input req = {0};
|
||||
struct hwrm_port_phy_i2c_read_output *output = bp->hwrm_cmd_resp_addr;
|
||||
int rc, byte_offset = 0;
|
||||
|
||||
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_I2C_READ, -1, -1);
|
||||
req.i2c_slave_addr = i2c_addr;
|
||||
req.page_number = cpu_to_le16(page_number);
|
||||
req.port_id = cpu_to_le16(bp->pf.port_id);
|
||||
do {
|
||||
u16 xfer_size;
|
||||
|
||||
xfer_size = min_t(u16, data_length, BNXT_MAX_PHY_I2C_RESP_SIZE);
|
||||
data_length -= xfer_size;
|
||||
req.page_offset = cpu_to_le16(start_addr + byte_offset);
|
||||
req.data_length = xfer_size;
|
||||
req.enables = cpu_to_le32(start_addr + byte_offset ?
|
||||
PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET : 0);
|
||||
mutex_lock(&bp->hwrm_cmd_lock);
|
||||
rc = _hwrm_send_message(bp, &req, sizeof(req),
|
||||
HWRM_CMD_TIMEOUT);
|
||||
if (!rc)
|
||||
memcpy(buf + byte_offset, output->data, xfer_size);
|
||||
mutex_unlock(&bp->hwrm_cmd_lock);
|
||||
byte_offset += xfer_size;
|
||||
} while (!rc && data_length > 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int bnxt_get_module_info(struct net_device *dev,
|
||||
struct ethtool_modinfo *modinfo)
|
||||
{
|
||||
struct bnxt *bp = netdev_priv(dev);
|
||||
struct hwrm_port_phy_i2c_read_input req = {0};
|
||||
struct hwrm_port_phy_i2c_read_output *output = bp->hwrm_cmd_resp_addr;
|
||||
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 >
|
||||
PORT_PHY_QCFG_RESP_MODULE_STATUS_WARNINGMSG)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* This feature is not supported in older firmware versions */
|
||||
if (bp->hwrm_spec_code < 0x10202)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_I2C_READ, -1, -1);
|
||||
req.i2c_slave_addr = I2C_DEV_ADDR_A0;
|
||||
req.page_number = 0;
|
||||
req.page_offset = cpu_to_le16(SFP_EEPROM_SFF_8472_COMP_ADDR);
|
||||
req.data_length = SFP_EEPROM_SFF_8472_COMP_SIZE;
|
||||
req.port_id = cpu_to_le16(bp->pf.port_id);
|
||||
mutex_lock(&bp->hwrm_cmd_lock);
|
||||
rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
|
||||
if (!rc) {
|
||||
u32 module_id = le32_to_cpu(output->data[0]);
|
||||
|
||||
switch (module_id) {
|
||||
case SFF_MODULE_ID_SFP:
|
||||
modinfo->type = ETH_MODULE_SFF_8472;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
|
||||
break;
|
||||
case SFF_MODULE_ID_QSFP:
|
||||
case SFF_MODULE_ID_QSFP_PLUS:
|
||||
modinfo->type = ETH_MODULE_SFF_8436;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
||||
break;
|
||||
case SFF_MODULE_ID_QSFP28:
|
||||
modinfo->type = ETH_MODULE_SFF_8636;
|
||||
modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
|
||||
break;
|
||||
default:
|
||||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&bp->hwrm_cmd_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int bnxt_get_module_eeprom(struct net_device *dev,
|
||||
struct ethtool_eeprom *eeprom,
|
||||
u8 *data)
|
||||
{
|
||||
struct bnxt *bp = netdev_priv(dev);
|
||||
u16 start = eeprom->offset, length = eeprom->len;
|
||||
int rc;
|
||||
|
||||
memset(data, 0, eeprom->len);
|
||||
|
||||
/* Read A0 portion of the EEPROM */
|
||||
if (start < ETH_MODULE_SFF_8436_LEN) {
|
||||
if (start + eeprom->len > ETH_MODULE_SFF_8436_LEN)
|
||||
length = ETH_MODULE_SFF_8436_LEN - start;
|
||||
rc = bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A0, 0,
|
||||
start, length, data);
|
||||
if (rc)
|
||||
return rc;
|
||||
start += length;
|
||||
data += length;
|
||||
length = eeprom->len - length;
|
||||
}
|
||||
|
||||
/* Read A2 portion of the EEPROM */
|
||||
if (length) {
|
||||
start -= ETH_MODULE_SFF_8436_LEN;
|
||||
bnxt_read_sfp_module_eeprom_info(bp, I2C_DEV_ADDR_A2, 1, start,
|
||||
length, data);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
const struct ethtool_ops bnxt_ethtool_ops = {
|
||||
.get_settings = bnxt_get_settings,
|
||||
.set_settings = bnxt_set_settings,
|
||||
|
@ -1528,4 +1647,6 @@ const struct ethtool_ops bnxt_ethtool_ops = {
|
|||
.get_link = bnxt_get_link,
|
||||
.get_eee = bnxt_get_eee,
|
||||
.set_eee = bnxt_set_eee,
|
||||
.get_module_info = bnxt_get_module_info,
|
||||
.get_module_eeprom = bnxt_get_module_eeprom,
|
||||
};
|
||||
|
|
|
@ -2093,6 +2093,40 @@ struct hwrm_port_phy_qcaps_output {
|
|||
#define PORT_PHY_QCAPS_RESP_VALID_SFT 24
|
||||
};
|
||||
|
||||
/* hwrm_port_phy_i2c_read */
|
||||
/* Input (40 bytes) */
|
||||
struct hwrm_port_phy_i2c_read_input {
|
||||
__le16 req_type;
|
||||
__le16 cmpl_ring;
|
||||
__le16 seq_id;
|
||||
__le16 target_id;
|
||||
__le64 resp_addr;
|
||||
__le32 flags;
|
||||
__le32 enables;
|
||||
#define PORT_PHY_I2C_READ_REQ_ENABLES_PAGE_OFFSET 0x1UL
|
||||
__le16 port_id;
|
||||
u8 i2c_slave_addr;
|
||||
u8 unused_0;
|
||||
__le16 page_number;
|
||||
__le16 page_offset;
|
||||
u8 data_length;
|
||||
u8 unused_1[7];
|
||||
};
|
||||
|
||||
/* Output (80 bytes) */
|
||||
struct hwrm_port_phy_i2c_read_output {
|
||||
__le16 error_code;
|
||||
__le16 req_type;
|
||||
__le16 seq_id;
|
||||
__le16 resp_len;
|
||||
__le32 data[16];
|
||||
__le32 unused_0;
|
||||
u8 unused_1;
|
||||
u8 unused_2;
|
||||
u8 unused_3;
|
||||
u8 valid;
|
||||
};
|
||||
|
||||
/* Input (24 bytes) */
|
||||
struct hwrm_queue_qportcfg_input {
|
||||
__le16 req_type;
|
||||
|
|
Loading…
Reference in New Issue