Commit a012dca9 authored by Scott W Taylor's avatar Scott W Taylor Committed by Jeff Kirsher

ice: add ethtool -m support for reading i2c eeprom modules

Implement ethtool -m support to read eeprom data from SFP/QSFP modules.
Signed-off-by: default avatarScott W Taylor <scott.w.taylor@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
Tested-by: default avatarAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 71c780f1
......@@ -1147,6 +1147,33 @@ struct ice_aqc_set_port_id_led {
u8 rsvd[13];
};
/* Read/Write SFF EEPROM command (indirect 0x06EE) */
struct ice_aqc_sff_eeprom {
u8 lport_num;
u8 lport_num_valid;
#define ICE_AQC_SFF_PORT_NUM_VALID BIT(0)
__le16 i2c_bus_addr;
#define ICE_AQC_SFF_I2CBUS_7BIT_M 0x7F
#define ICE_AQC_SFF_I2CBUS_10BIT_M 0x3FF
#define ICE_AQC_SFF_I2CBUS_TYPE_M BIT(10)
#define ICE_AQC_SFF_I2CBUS_TYPE_7BIT 0
#define ICE_AQC_SFF_I2CBUS_TYPE_10BIT ICE_AQC_SFF_I2CBUS_TYPE_M
#define ICE_AQC_SFF_SET_EEPROM_PAGE_S 11
#define ICE_AQC_SFF_SET_EEPROM_PAGE_M (0x3 << ICE_AQC_SFF_SET_EEPROM_PAGE_S)
#define ICE_AQC_SFF_NO_PAGE_CHANGE 0
#define ICE_AQC_SFF_SET_23_ON_MISMATCH 1
#define ICE_AQC_SFF_SET_22_ON_MISMATCH 2
#define ICE_AQC_SFF_IS_WRITE BIT(15)
__le16 i2c_mem_addr;
__le16 eeprom_page;
#define ICE_AQC_SFF_EEPROM_BANK_S 0
#define ICE_AQC_SFF_EEPROM_BANK_M (0xFF << ICE_AQC_SFF_EEPROM_BANK_S)
#define ICE_AQC_SFF_EEPROM_PAGE_S 8
#define ICE_AQC_SFF_EEPROM_PAGE_M (0xFF << ICE_AQC_SFF_EEPROM_PAGE_S)
__le32 addr_high;
__le32 addr_low;
};
/* NVM Read command (indirect 0x0701)
* NVM Erase commands (direct 0x0702)
* NVM Update commands (indirect 0x0703)
......@@ -1618,6 +1645,7 @@ struct ice_aq_desc {
struct ice_aqc_get_phy_caps get_phy;
struct ice_aqc_set_phy_cfg set_phy;
struct ice_aqc_restart_an restart_an;
struct ice_aqc_sff_eeprom read_write_sff_param;
struct ice_aqc_set_port_id_led set_port_id_led;
struct ice_aqc_get_sw_cfg get_sw_conf;
struct ice_aqc_sw_rules sw_rules;
......@@ -1741,6 +1769,7 @@ enum ice_adminq_opc {
ice_aqc_opc_set_event_mask = 0x0613,
ice_aqc_opc_set_mac_lb = 0x0620,
ice_aqc_opc_set_port_id_led = 0x06E9,
ice_aqc_opc_sff_eeprom = 0x06EE,
/* NVM commands */
ice_aqc_opc_nvm_read = 0x0701,
......
......@@ -2555,6 +2555,52 @@ ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode,
return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
}
/**
* ice_aq_sff_eeprom
* @hw: pointer to the HW struct
* @lport: bits [7:0] = logical port, bit [8] = logical port valid
* @bus_addr: I2C bus address of the eeprom (typically 0xA0, 0=topo default)
* @mem_addr: I2C offset. lower 8 bits for address, 8 upper bits zero padding.
* @page: QSFP page
* @set_page: set or ignore the page
* @data: pointer to data buffer to be read/written to the I2C device.
* @length: 1-16 for read, 1 for write.
* @write: 0 read, 1 for write.
* @cd: pointer to command details structure or NULL
*
* Read/Write SFF EEPROM (0x06EE)
*/
enum ice_status
ice_aq_sff_eeprom(struct ice_hw *hw, u16 lport, u8 bus_addr,
u16 mem_addr, u8 page, u8 set_page, u8 *data, u8 length,
bool write, struct ice_sq_cd *cd)
{
struct ice_aqc_sff_eeprom *cmd;
struct ice_aq_desc desc;
enum ice_status status;
if (!data || (mem_addr & 0xff00))
return ICE_ERR_PARAM;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_sff_eeprom);
cmd = &desc.params.read_write_sff_param;
desc.flags = cpu_to_le16(ICE_AQ_FLAG_RD | ICE_AQ_FLAG_BUF);
cmd->lport_num = (u8)(lport & 0xff);
cmd->lport_num_valid = (u8)((lport >> 8) & 0x01);
cmd->i2c_bus_addr = cpu_to_le16(((bus_addr >> 1) &
ICE_AQC_SFF_I2CBUS_7BIT_M) |
((set_page <<
ICE_AQC_SFF_SET_EEPROM_PAGE_S) &
ICE_AQC_SFF_SET_EEPROM_PAGE_M));
cmd->i2c_mem_addr = cpu_to_le16(mem_addr & 0xff);
cmd->eeprom_page = cpu_to_le16((u16)page << ICE_AQC_SFF_EEPROM_PAGE_S);
if (write)
cmd->i2c_bus_addr |= cpu_to_le16(ICE_AQC_SFF_IS_WRITE);
status = ice_aq_send_cmd(hw, &desc, data, length, cd);
return status;
}
/**
* __ice_aq_get_set_rss_lut
* @hw: pointer to the hardware structure
......
......@@ -117,6 +117,10 @@ ice_aq_set_mac_loopback(struct ice_hw *hw, bool ena_lpbk, struct ice_sq_cd *cd);
enum ice_status
ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode,
struct ice_sq_cd *cd);
enum ice_status
ice_aq_sff_eeprom(struct ice_hw *hw, u16 lport, u8 bus_addr,
u16 mem_addr, u8 page, u8 set_page, u8 *data, u8 length,
bool write, struct ice_sq_cd *cd);
enum ice_status
ice_dis_vsi_txq(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u8 num_queues,
......
......@@ -3455,6 +3455,151 @@ ice_set_per_q_coalesce(struct net_device *netdev, u32 q_num,
return __ice_set_coalesce(netdev, ec, q_num);
}
#define ICE_I2C_EEPROM_DEV_ADDR 0xA0
#define ICE_I2C_EEPROM_DEV_ADDR2 0xA2
#define ICE_MODULE_TYPE_SFP 0x03
#define ICE_MODULE_TYPE_QSFP_PLUS 0x0D
#define ICE_MODULE_TYPE_QSFP28 0x11
#define ICE_MODULE_SFF_ADDR_MODE 0x04
#define ICE_MODULE_SFF_DIAG_CAPAB 0x40
#define ICE_MODULE_REVISION_ADDR 0x01
#define ICE_MODULE_SFF_8472_COMP 0x5E
#define ICE_MODULE_SFF_8472_SWAP 0x5C
#define ICE_MODULE_QSFP_MAX_LEN 640
/**
* ice_get_module_info - get SFF module type and revision information
* @netdev: network interface device structure
* @modinfo: module EEPROM size and layout information structure
*/
static int
ice_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
{
struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_vsi *vsi = np->vsi;
struct ice_pf *pf = vsi->back;
struct ice_hw *hw = &pf->hw;
enum ice_status status;
u8 sff8472_comp = 0;
u8 sff8472_swap = 0;
u8 sff8636_rev = 0;
u8 value = 0;
status = ice_aq_sff_eeprom(hw, 0, ICE_I2C_EEPROM_DEV_ADDR, 0x00, 0x00,
0, &value, 1, 0, NULL);
if (status)
return -EIO;
switch (value) {
case ICE_MODULE_TYPE_SFP:
status = ice_aq_sff_eeprom(hw, 0, ICE_I2C_EEPROM_DEV_ADDR,
ICE_MODULE_SFF_8472_COMP, 0x00, 0,
&sff8472_comp, 1, 0, NULL);
if (status)
return -EIO;
status = ice_aq_sff_eeprom(hw, 0, ICE_I2C_EEPROM_DEV_ADDR,
ICE_MODULE_SFF_8472_SWAP, 0x00, 0,
&sff8472_swap, 1, 0, NULL);
if (status)
return -EIO;
if (sff8472_swap & ICE_MODULE_SFF_ADDR_MODE) {
modinfo->type = ETH_MODULE_SFF_8079;
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
} else if (sff8472_comp &&
(sff8472_swap & ICE_MODULE_SFF_DIAG_CAPAB)) {
modinfo->type = ETH_MODULE_SFF_8472;
modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
} else {
modinfo->type = ETH_MODULE_SFF_8079;
modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
}
break;
case ICE_MODULE_TYPE_QSFP_PLUS:
case ICE_MODULE_TYPE_QSFP28:
status = ice_aq_sff_eeprom(hw, 0, ICE_I2C_EEPROM_DEV_ADDR,
ICE_MODULE_REVISION_ADDR, 0x00, 0,
&sff8636_rev, 1, 0, NULL);
if (status)
return -EIO;
/* Check revision compliance */
if (sff8636_rev > 0x02) {
/* Module is SFF-8636 compliant */
modinfo->type = ETH_MODULE_SFF_8636;
modinfo->eeprom_len = ICE_MODULE_QSFP_MAX_LEN;
} else {
modinfo->type = ETH_MODULE_SFF_8436;
modinfo->eeprom_len = ICE_MODULE_QSFP_MAX_LEN;
}
break;
default:
netdev_warn(netdev,
"SFF Module Type not recognized.\n");
return -EINVAL;
}
return 0;
}
/**
* ice_get_module_eeprom - fill buffer with SFF EEPROM contents
* @netdev: network interface device structure
* @ee: EEPROM dump request structure
* @data: buffer to be filled with EEPROM contents
*/
static int
ice_get_module_eeprom(struct net_device *netdev,
struct ethtool_eeprom *ee, u8 *data)
{
struct ice_netdev_priv *np = netdev_priv(netdev);
u8 addr = ICE_I2C_EEPROM_DEV_ADDR;
struct ice_vsi *vsi = np->vsi;
struct ice_pf *pf = vsi->back;
struct ice_hw *hw = &pf->hw;
enum ice_status status;
bool is_sfp = false;
u16 offset = 0;
u8 value = 0;
u8 page = 0;
int i;
status = ice_aq_sff_eeprom(hw, 0, addr, offset, page, 0,
&value, 1, 0, NULL);
if (status)
return -EIO;
if (!ee || !ee->len || !data)
return -EINVAL;
if (value == ICE_MODULE_TYPE_SFP)
is_sfp = true;
for (i = 0; i < ee->len; i++) {
offset = i + ee->offset;
/* Check if we need to access the other memory page */
if (is_sfp) {
if (offset >= ETH_MODULE_SFF_8079_LEN) {
offset -= ETH_MODULE_SFF_8079_LEN;
addr = ICE_I2C_EEPROM_DEV_ADDR2;
}
} else {
while (offset >= ETH_MODULE_SFF_8436_LEN) {
/* Compute memory page number and offset. */
offset -= ETH_MODULE_SFF_8436_LEN / 2;
page++;
}
}
status = ice_aq_sff_eeprom(hw, 0, addr, offset, page, !is_sfp,
&value, 1, 0, NULL);
if (status)
value = 0;
data[i] = value;
}
return 0;
}
static const struct ethtool_ops ice_ethtool_ops = {
.get_link_ksettings = ice_get_link_ksettings,
.set_link_ksettings = ice_set_link_ksettings,
......@@ -3490,6 +3635,8 @@ static const struct ethtool_ops ice_ethtool_ops = {
.set_per_queue_coalesce = ice_set_per_q_coalesce,
.get_fecparam = ice_get_fecparam,
.set_fecparam = ice_set_fecparam,
.get_module_info = ice_get_module_info,
.get_module_eeprom = ice_get_module_eeprom,
};
static const struct ethtool_ops ice_ethtool_safe_mode_ops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment