diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c')
| -rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 377 | 
1 files changed, 316 insertions, 61 deletions
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index edfa67adf2f..ce1a9161867 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1364,11 +1364,27 @@ static int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf,  	return rc;  } +static int bnx2x_nvram_read32(struct bnx2x *bp, u32 offset, u32 *buf, +			      int buf_size) +{ +	int rc; + +	rc = bnx2x_nvram_read(bp, offset, (u8 *)buf, buf_size); + +	if (!rc) { +		__be32 *be = (__be32 *)buf; + +		while ((buf_size -= 4) >= 0) +			*buf++ = be32_to_cpu(*be++); +	} + +	return rc; +} +  static int bnx2x_get_eeprom(struct net_device *dev,  			    struct ethtool_eeprom *eeprom, u8 *eebuf)  {  	struct bnx2x *bp = netdev_priv(dev); -	int rc;  	if (!netif_running(dev)) {  		DP(BNX2X_MSG_ETHTOOL  | BNX2X_MSG_NVM, @@ -1383,9 +1399,7 @@ static int bnx2x_get_eeprom(struct net_device *dev,  	/* parameters already validated in ethtool_get_eeprom */ -	rc = bnx2x_nvram_read(bp, eeprom->offset, eebuf, eeprom->len); - -	return rc; +	return bnx2x_nvram_read(bp, eeprom->offset, eebuf, eeprom->len);  }  static int bnx2x_get_module_eeprom(struct net_device *dev, @@ -1393,10 +1407,9 @@ static int bnx2x_get_module_eeprom(struct net_device *dev,  				   u8 *data)  {  	struct bnx2x *bp = netdev_priv(dev); -	int rc = 0, phy_idx; +	int rc = -EINVAL, phy_idx;  	u8 *user_data = data; -	int remaining_len = ee->len, xfer_size; -	unsigned int page_off = ee->offset; +	unsigned int start_addr = ee->offset, xfer_size = 0;  	if (!netif_running(dev)) {  		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, @@ -1405,21 +1418,52 @@ static int bnx2x_get_module_eeprom(struct net_device *dev,  	}  	phy_idx = bnx2x_get_cur_phy_idx(bp); -	bnx2x_acquire_phy_lock(bp); -	while (!rc && remaining_len > 0) { -		xfer_size = (remaining_len > SFP_EEPROM_PAGE_SIZE) ? -			SFP_EEPROM_PAGE_SIZE : remaining_len; + +	/* Read A0 section */ +	if (start_addr < ETH_MODULE_SFF_8079_LEN) { +		/* Limit transfer size to the A0 section boundary */ +		if (start_addr + ee->len > ETH_MODULE_SFF_8079_LEN) +			xfer_size = ETH_MODULE_SFF_8079_LEN - start_addr; +		else +			xfer_size = ee->len; +		bnx2x_acquire_phy_lock(bp);  		rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx],  						  &bp->link_params, -						  page_off, +						  I2C_DEV_ADDR_A0, +						  start_addr,  						  xfer_size,  						  user_data); -		remaining_len -= xfer_size; +		bnx2x_release_phy_lock(bp); +		if (rc) { +			DP(BNX2X_MSG_ETHTOOL, "Failed reading A0 section\n"); + +			return -EINVAL; +		}  		user_data += xfer_size; -		page_off += xfer_size; +		start_addr += xfer_size;  	} -	bnx2x_release_phy_lock(bp); +	/* Read A2 section */ +	if ((start_addr >= ETH_MODULE_SFF_8079_LEN) && +	    (start_addr < ETH_MODULE_SFF_8472_LEN)) { +		xfer_size = ee->len - xfer_size; +		/* Limit transfer size to the A2 section boundary */ +		if (start_addr + xfer_size > ETH_MODULE_SFF_8472_LEN) +			xfer_size = ETH_MODULE_SFF_8472_LEN - start_addr; +		start_addr -= ETH_MODULE_SFF_8079_LEN; +		bnx2x_acquire_phy_lock(bp); +		rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx], +						  &bp->link_params, +						  I2C_DEV_ADDR_A2, +						  start_addr, +						  xfer_size, +						  user_data); +		bnx2x_release_phy_lock(bp); +		if (rc) { +			DP(BNX2X_MSG_ETHTOOL, "Failed reading A2 section\n"); +			return -EINVAL; +		} +	}  	return rc;  } @@ -1427,24 +1471,50 @@ static int bnx2x_get_module_info(struct net_device *dev,  				 struct ethtool_modinfo *modinfo)  {  	struct bnx2x *bp = netdev_priv(dev); -	int phy_idx; +	int phy_idx, rc; +	u8 sff8472_comp, diag_type; +  	if (!netif_running(dev)) { -		DP(BNX2X_MSG_ETHTOOL  | BNX2X_MSG_NVM, +		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,  		   "cannot access eeprom when the interface is down\n");  		return -EAGAIN;  	} -  	phy_idx = bnx2x_get_cur_phy_idx(bp); -	switch (bp->link_params.phy[phy_idx].media_type) { -	case ETH_PHY_SFPP_10G_FIBER: -	case ETH_PHY_SFP_1G_FIBER: -	case ETH_PHY_DA_TWINAX: +	bnx2x_acquire_phy_lock(bp); +	rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx], +					  &bp->link_params, +					  I2C_DEV_ADDR_A0, +					  SFP_EEPROM_SFF_8472_COMP_ADDR, +					  SFP_EEPROM_SFF_8472_COMP_SIZE, +					  &sff8472_comp); +	bnx2x_release_phy_lock(bp); +	if (rc) { +		DP(BNX2X_MSG_ETHTOOL, "Failed reading SFF-8472 comp field\n"); +		return -EINVAL; +	} + +	bnx2x_acquire_phy_lock(bp); +	rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx], +					  &bp->link_params, +					  I2C_DEV_ADDR_A0, +					  SFP_EEPROM_DIAG_TYPE_ADDR, +					  SFP_EEPROM_DIAG_TYPE_SIZE, +					  &diag_type); +	bnx2x_release_phy_lock(bp); +	if (rc) { +		DP(BNX2X_MSG_ETHTOOL, "Failed reading Diag Type field\n"); +		return -EINVAL; +	} + +	if (!sff8472_comp || +	    (diag_type & SFP_EEPROM_DIAG_ADDR_CHANGE_REQ)) {  		modinfo->type = ETH_MODULE_SFF_8079;  		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; -		return 0; -	default: -		return -EOPNOTSUPP; +	} else { +		modinfo->type = ETH_MODULE_SFF_8472; +		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;  	} +	return 0;  }  static int bnx2x_nvram_write_dword(struct bnx2x *bp, u32 offset, u32 val, @@ -1496,9 +1566,8 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,  			      int buf_size)  {  	int rc; -	u32 cmd_flags; -	u32 align_offset; -	__be32 val; +	u32 cmd_flags, align_offset, val; +	__be32 val_be;  	if (offset + buf_size > bp->common.flash_size) {  		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, @@ -1517,16 +1586,16 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf,  	cmd_flags = (MCPR_NVM_COMMAND_FIRST | MCPR_NVM_COMMAND_LAST);  	align_offset = (offset & ~0x03); -	rc = bnx2x_nvram_read_dword(bp, align_offset, &val, cmd_flags); +	rc = bnx2x_nvram_read_dword(bp, align_offset, &val_be, cmd_flags);  	if (rc == 0) { -		val &= ~(0xff << BYTE_OFFSET(offset)); -		val |= (*data_buf << BYTE_OFFSET(offset)); -  		/* nvram data is returned as an array of bytes  		 * convert it back to cpu order  		 */ -		val = be32_to_cpu(val); +		val = be32_to_cpu(val_be); + +		val &= ~le32_to_cpu(0xff << BYTE_OFFSET(offset)); +		val |= le32_to_cpu(*data_buf << BYTE_OFFSET(offset));  		rc = bnx2x_nvram_write_dword(bp, align_offset, val,  					     cmd_flags); @@ -1820,12 +1889,15 @@ static int bnx2x_set_pauseparam(struct net_device *dev,  			bp->link_params.req_flow_ctrl[cfg_idx] =  				BNX2X_FLOW_CTRL_AUTO;  		} -		bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_NONE; +		bp->link_params.req_fc_auto_adv = 0;  		if (epause->rx_pause)  			bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_RX;  		if (epause->tx_pause)  			bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_TX; + +		if (!bp->link_params.req_fc_auto_adv) +			bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_NONE;  	}  	DP(BNX2X_MSG_ETHTOOL, @@ -2526,14 +2598,168 @@ static int bnx2x_test_ext_loopback(struct bnx2x *bp)  	return rc;  } +struct code_entry { +	u32 sram_start_addr; +	u32 code_attribute; +#define CODE_IMAGE_TYPE_MASK			0xf0800003 +#define CODE_IMAGE_VNTAG_PROFILES_DATA		0xd0000003 +#define CODE_IMAGE_LENGTH_MASK			0x007ffffc +#define CODE_IMAGE_TYPE_EXTENDED_DIR		0xe0000000 +	u32 nvm_start_addr; +}; + +#define CODE_ENTRY_MAX			16 +#define CODE_ENTRY_EXTENDED_DIR_IDX	15 +#define MAX_IMAGES_IN_EXTENDED_DIR	64 +#define NVRAM_DIR_OFFSET		0x14 + +#define EXTENDED_DIR_EXISTS(code)					  \ +	((code & CODE_IMAGE_TYPE_MASK) == CODE_IMAGE_TYPE_EXTENDED_DIR && \ +	 (code & CODE_IMAGE_LENGTH_MASK) != 0) +  #define CRC32_RESIDUAL			0xdebb20e3 +#define CRC_BUFF_SIZE			256 + +static int bnx2x_nvram_crc(struct bnx2x *bp, +			   int offset, +			   int size, +			   u8 *buff) +{ +	u32 crc = ~0; +	int rc = 0, done = 0; + +	DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, +	   "NVRAM CRC from 0x%08x to 0x%08x\n", offset, offset + size); + +	while (done < size) { +		int count = min_t(int, size - done, CRC_BUFF_SIZE); + +		rc = bnx2x_nvram_read(bp, offset + done, buff, count); + +		if (rc) +			return rc; + +		crc = crc32_le(crc, buff, count); +		done += count; +	} + +	if (crc != CRC32_RESIDUAL) +		rc = -EINVAL; + +	return rc; +} + +static int bnx2x_test_nvram_dir(struct bnx2x *bp, +				struct code_entry *entry, +				u8 *buff) +{ +	size_t size = entry->code_attribute & CODE_IMAGE_LENGTH_MASK; +	u32 type = entry->code_attribute & CODE_IMAGE_TYPE_MASK; +	int rc; + +	/* Zero-length images and AFEX profiles do not have CRC */ +	if (size == 0 || type == CODE_IMAGE_VNTAG_PROFILES_DATA) +		return 0; + +	rc = bnx2x_nvram_crc(bp, entry->nvm_start_addr, size, buff); +	if (rc) +		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, +		   "image %x has failed crc test (rc %d)\n", type, rc); + +	return rc; +} + +static int bnx2x_test_dir_entry(struct bnx2x *bp, u32 addr, u8 *buff) +{ +	int rc; +	struct code_entry entry; + +	rc = bnx2x_nvram_read32(bp, addr, (u32 *)&entry, sizeof(entry)); +	if (rc) +		return rc; + +	return bnx2x_test_nvram_dir(bp, &entry, buff); +} + +static int bnx2x_test_nvram_ext_dirs(struct bnx2x *bp, u8 *buff) +{ +	u32 rc, cnt, dir_offset = NVRAM_DIR_OFFSET; +	struct code_entry entry; +	int i; + +	rc = bnx2x_nvram_read32(bp, +				dir_offset + +				sizeof(entry) * CODE_ENTRY_EXTENDED_DIR_IDX, +				(u32 *)&entry, sizeof(entry)); +	if (rc) +		return rc; + +	if (!EXTENDED_DIR_EXISTS(entry.code_attribute)) +		return 0; + +	rc = bnx2x_nvram_read32(bp, entry.nvm_start_addr, +				&cnt, sizeof(u32)); +	if (rc) +		return rc; + +	dir_offset = entry.nvm_start_addr + 8; + +	for (i = 0; i < cnt && i < MAX_IMAGES_IN_EXTENDED_DIR; i++) { +		rc = bnx2x_test_dir_entry(bp, dir_offset + +					      sizeof(struct code_entry) * i, +					  buff); +		if (rc) +			return rc; +	} + +	return 0; +} + +static int bnx2x_test_nvram_dirs(struct bnx2x *bp, u8 *buff) +{ +	u32 rc, dir_offset = NVRAM_DIR_OFFSET; +	int i; + +	DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "NVRAM DIRS CRC test-set\n"); + +	for (i = 0; i < CODE_ENTRY_EXTENDED_DIR_IDX; i++) { +		rc = bnx2x_test_dir_entry(bp, dir_offset + +					      sizeof(struct code_entry) * i, +					  buff); +		if (rc) +			return rc; +	} + +	return bnx2x_test_nvram_ext_dirs(bp, buff); +} + +struct crc_pair { +	int offset; +	int size; +}; + +static int bnx2x_test_nvram_tbl(struct bnx2x *bp, +				const struct crc_pair *nvram_tbl, u8 *buf) +{ +	int i; + +	for (i = 0; nvram_tbl[i].size; i++) { +		int rc = bnx2x_nvram_crc(bp, nvram_tbl[i].offset, +					 nvram_tbl[i].size, buf); +		if (rc) { +			DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, +			   "nvram_tbl[%d] has failed crc test (rc %d)\n", +			   i, rc); +			return rc; +		} +	} + +	return 0; +}  static int bnx2x_test_nvram(struct bnx2x *bp)  { -	static const struct { -		int offset; -		int size; -	} nvram_tbl[] = { +	const struct crc_pair nvram_tbl[] = {  		{     0,  0x14 }, /* bootstrap */  		{  0x14,  0xec }, /* dir */  		{ 0x100, 0x350 }, /* manuf_info */ @@ -2542,30 +2768,33 @@ static int bnx2x_test_nvram(struct bnx2x *bp)  		{ 0x708,  0x70 }, /* manuf_key_info */  		{     0,     0 }  	}; -	__be32 *buf; -	u8 *data; -	int i, rc; -	u32 magic, crc; +	const struct crc_pair nvram_tbl2[] = { +		{ 0x7e8, 0x350 }, /* manuf_info2 */ +		{ 0xb38,  0xf0 }, /* feature_info */ +		{     0,     0 } +	}; + +	u8 *buf; +	int rc; +	u32 magic;  	if (BP_NOMCP(bp))  		return 0; -	buf = kmalloc(0x350, GFP_KERNEL); +	buf = kmalloc(CRC_BUFF_SIZE, GFP_KERNEL);  	if (!buf) {  		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "kmalloc failed\n");  		rc = -ENOMEM;  		goto test_nvram_exit;  	} -	data = (u8 *)buf; -	rc = bnx2x_nvram_read(bp, 0, data, 4); +	rc = bnx2x_nvram_read32(bp, 0, &magic, sizeof(magic));  	if (rc) {  		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,  		   "magic value read (rc %d)\n", rc);  		goto test_nvram_exit;  	} -	magic = be32_to_cpu(buf[0]);  	if (magic != 0x669955aa) {  		DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM,  		   "wrong magic value (0x%08x)\n", magic); @@ -2573,25 +2802,26 @@ static int bnx2x_test_nvram(struct bnx2x *bp)  		goto test_nvram_exit;  	} -	for (i = 0; nvram_tbl[i].size; i++) { +	DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "Port 0 CRC test-set\n"); +	rc = bnx2x_test_nvram_tbl(bp, nvram_tbl, buf); +	if (rc) +		goto test_nvram_exit; -		rc = bnx2x_nvram_read(bp, nvram_tbl[i].offset, data, -				      nvram_tbl[i].size); -		if (rc) { -			DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, -			   "nvram_tbl[%d] read data (rc %d)\n", i, rc); -			goto test_nvram_exit; -		} +	if (!CHIP_IS_E1x(bp) && !CHIP_IS_57811xx(bp)) { +		u32 hide = SHMEM_RD(bp, dev_info.shared_hw_config.config2) & +			   SHARED_HW_CFG_HIDE_PORT1; -		crc = ether_crc_le(nvram_tbl[i].size, data); -		if (crc != CRC32_RESIDUAL) { +		if (!hide) {  			DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, -			   "nvram_tbl[%d] wrong crc value (0x%08x)\n", i, crc); -			rc = -ENODEV; -			goto test_nvram_exit; +			   "Port 1 CRC test-set\n"); +			rc = bnx2x_test_nvram_tbl(bp, nvram_tbl2, buf); +			if (rc) +				goto test_nvram_exit;  		}  	} +	rc = bnx2x_test_nvram_dirs(bp, buf); +  test_nvram_exit:  	kfree(buf);  	return rc; @@ -3232,7 +3462,32 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {  	.get_ts_info		= ethtool_op_get_ts_info,  }; -void bnx2x_set_ethtool_ops(struct net_device *netdev) +static const struct ethtool_ops bnx2x_vf_ethtool_ops = { +	.get_settings		= bnx2x_get_settings, +	.set_settings		= bnx2x_set_settings, +	.get_drvinfo		= bnx2x_get_drvinfo, +	.get_msglevel		= bnx2x_get_msglevel, +	.set_msglevel		= bnx2x_set_msglevel, +	.get_link		= bnx2x_get_link, +	.get_coalesce		= bnx2x_get_coalesce, +	.get_ringparam		= bnx2x_get_ringparam, +	.set_ringparam		= bnx2x_set_ringparam, +	.get_sset_count		= bnx2x_get_sset_count, +	.get_strings		= bnx2x_get_strings, +	.get_ethtool_stats	= bnx2x_get_ethtool_stats, +	.get_rxnfc		= bnx2x_get_rxnfc, +	.set_rxnfc		= bnx2x_set_rxnfc, +	.get_rxfh_indir_size	= bnx2x_get_rxfh_indir_size, +	.get_rxfh_indir		= bnx2x_get_rxfh_indir, +	.set_rxfh_indir		= bnx2x_set_rxfh_indir, +	.get_channels		= bnx2x_get_channels, +	.set_channels		= bnx2x_set_channels, +}; + +void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev)  { -	SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops); +	if (IS_PF(bp)) +		SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops); +	else /* vf */ +		SET_ETHTOOL_OPS(netdev, &bnx2x_vf_ethtool_ops);  }  |