[Mellanox] Credo Y-cable read_eeprom/write_eeprom API implementation (#10320)

- Why I did it
Implement read_eeprom/write_eeprom API for Credo Y-cable for Dual ToR Active-Standby

- How I did it
Use mlxreg utility for API implementation

Signed-off-by: Andriy Yurkiv <ayurkiv@nvidia.com>
This commit is contained in:
Andriy Yurkiv 2022-03-30 20:41:31 +03:00 committed by GitHub
parent 761ae24427
commit 1e2e493daa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 247 additions and 1 deletions

View File

@ -121,6 +121,13 @@ CPU_MASK = PORT_TYPE_MASK & (PORT_TYPE_CPU << PORT_TYPE_OFFSET)
# parameters for SFP presence
SFP_STATUS_INSERTED = '1'
# SFP constants
SFP_PAGE_SIZE = 256
SFP_UPPER_PAGE_OFFSET = 128
SFP_VENDOR_PAGE_START = 640
BYTES_IN_DWORD = 4
# Global logger class instance
logger = Logger()
@ -146,6 +153,72 @@ def deinitialize_sdk_handle(sdk_handle):
logger.log_warning("Sdk handle is none")
return False
class MlxregManager:
def __init__(self, mst_pci_device, slot_id, sdk_index):
self.mst_pci_device = mst_pci_device
self.slot_id = slot_id
self.sdk_index = sdk_index
def construct_dword(self, write_buffer):
if len(write_buffer) == 0:
return None
used_bytes_in_dword = len(write_buffer) % BYTES_IN_DWORD
res = "dword[0]=0x"
for idx, x in enumerate(write_buffer):
word = hex(x)[2:]
if (idx > 0) and (idx % BYTES_IN_DWORD) == 0:
res += ",dword[{}]=0x".format(str((idx + 1)//BYTES_IN_DWORD))
res += word.zfill(2)
if used_bytes_in_dword > 0:
res += (BYTES_IN_DWORD - used_bytes_in_dword) * "00"
return res
def write_mlxreg_eeprom(self, num_bytes, dword, device_address, page):
if not dword:
return False
try:
cmd = "mlxreg -d /dev/mst/{} --reg_name MCIA --indexes \
slot_index={},module={},device_address={},page_number={},i2c_device_address=0x50,size={},bank_number=0 \
--set {} -y".format(self.mst_pci_device, self.slot_id, self.sdk_index, device_address, page, num_bytes, dword)
subprocess.check_call(cmd, shell=True, universal_newlines=True, stdout=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
logger.log_error("Error! Unable to write data for {} port, page {} offset {}, rc = {}, err msg: {}".format(self.sdk_index, page, device_address, e.returncode, e.output))
return False
return True
def read_mlxred_eeprom(self, offset, page, num_bytes):
try:
cmd = "mlxreg -d /dev/mst/{} --reg_name MCIA --indexes \
slot_index={},module={},device_address={},page_number={},i2c_device_address=0x50,size={},bank_number=0 \
--get".format(self.mst_pci_device, self.slot_id, self.sdk_index, offset, page, num_bytes)
result = subprocess.check_output(cmd, universal_newlines=True, shell=True)
except subprocess.CalledProcessError as e:
logger.log_error("Error! Unable to write data for {} port, page {} offset {}, rc = {}, err msg: {}".format(self.sdk_index, page, device_address, e.returncode, e.output))
return None
return result
def parse_mlxreg_read_output(self, read_output, num_bytes):
res = ""
dword_num = num_bytes // BYTES_IN_DWORD
used_bytes_in_dword = num_bytes % BYTES_IN_DWORD
arr = [value for value in read_output.split('\n') if value[0:5] == "dword"]
for i in range(dword_num):
dword = arr[i].split()[2]
res += dword[2:]
if used_bytes_in_dword > 0:
# Cut needed info and insert into final hex string
# Example: 3 bytes : 0x12345600
# ^ ^
dword = arr[dword_num].split()[2]
res += dword[2 : 2 + used_bytes_in_dword * 2]
return bytearray.fromhex(res) if res else None
class SdkHandleContext(object):
def __init__(self):
@ -194,6 +267,16 @@ class SFP(SfpOptoeBase):
self._thermal_list = initialize_linecard_sfp_thermal(lc_name, slot_id, sfp_index)
self.slot_id = slot_id
self.mst_pci_device = self.get_mst_pci_device()
# get MST PCI device name
def get_mst_pci_device(self):
device_name = None
try:
device_name = subprocess.check_output("ls /dev/mst/ | grep pciconf", universal_newlines=True, shell=True).strip()
except subprocess.CalledProcessError as e:
logger.log_error("Failed to find mst PCI device rc={} err.msg={}".format(e.returncode, e.output))
return device_name
@property
def sdk_handle(self):
@ -222,7 +305,11 @@ class SFP(SfpOptoeBase):
return eeprom_raw is not None
# Read out any bytes from any offset
def read_eeprom(self, offset, num_bytes):
def _read_eeprom_specific_bytes(self, offset, num_bytes):
if offset + num_bytes > SFP_VENDOR_PAGE_START:
logger.log_error("Error mismatch between page size and bytes to read (offset: {} num_bytes: {}) ".format(offset, num_bytes))
return None
eeprom_raw = []
ethtool_cmd = "ethtool -m sfp{} hex on offset {} length {}".format(self.index, offset, num_bytes)
try:
@ -241,6 +328,81 @@ class SFP(SfpOptoeBase):
eeprom_raw = list(map(lambda h: int(h, base=16), eeprom_raw))
return bytearray(eeprom_raw)
# read eeprom specfic bytes beginning from offset with size as num_bytes
def read_eeprom(self, offset, num_bytes):
"""
Read eeprom specfic bytes beginning from a random offset with size as num_bytes
Returns:
bytearray, if raw sequence of bytes are read correctly from the offset of size num_bytes
None, if the read_eeprom fails
Example:
mlxreg -d /dev/mst/mt52100_pciconf0 --reg_name MCIA --indexes slot_index=0,module=1,device_address=148,page_number=0,i2c_device_address=0x50,size=16,bank_number=0 -g
Sending access register...
Field Name | Data
===================================
status | 0x00000000
slot_index | 0x00000000
module | 0x00000001
l | 0x00000000
device_address | 0x00000094
page_number | 0x00000000
i2c_device_address | 0x00000050
size | 0x00000010
bank_number | 0x00000000
dword[0] | 0x43726564
dword[1] | 0x6f202020
dword[2] | 0x20202020
dword[3] | 0x20202020
dword[4] | 0x00000000
dword[5] | 0x00000000
....
16 bytes to read from dword -> 0x437265646f2020202020202020202020 -> Credo
"""
# recalculate offset and page. Use 'ethtool' if there is no need to read vendor pages
if offset < SFP_VENDOR_PAGE_START:
return self._read_eeprom_specific_bytes(offset, num_bytes)
else:
page = (offset - SFP_PAGE_SIZE) // SFP_UPPER_PAGE_OFFSET + 1
# calculate offset per page
device_address = (offset - SFP_PAGE_SIZE) % SFP_UPPER_PAGE_OFFSET + SFP_UPPER_PAGE_OFFSET
if not self.mst_pci_device:
return None
mlxreg_mngr = MlxregManager(self.mst_pci_device, self.slot_id, self.sdk_index)
read_output = mlxreg_mngr.read_mlxred_eeprom(device_address, page, num_bytes)
return mlxreg_mngr.parse_mlxreg_read_output(read_output, num_bytes)
# write eeprom specfic bytes beginning from offset with size as num_bytes
def write_eeprom(self, offset, num_bytes, write_buffer):
"""
write eeprom specfic bytes beginning from a random offset with size as num_bytes
and write_buffer as the required bytes
Returns:
Boolean, true if the write succeeded and false if it did not succeed.
Example:
mlxreg -d /dev/mst/mt52100_pciconf0 --reg_name MCIA --indexes slot_index=0,module=1,device_address=154,page_number=5,i2c_device_address=0x50,size=1,bank_number=0 --set dword[0]=0x01000000 -y
"""
if num_bytes != len(write_buffer):
logger.log_error("Error mismatch between buffer length and number of bytes to be written")
return False
# recalculate offset and page
if offset < SFP_PAGE_SIZE:
page = 0
device_address = offset
else:
page = (offset - SFP_PAGE_SIZE) // SFP_UPPER_PAGE_OFFSET + 1
# calculate offset per page
device_address = (offset - SFP_PAGE_SIZE) % SFP_UPPER_PAGE_OFFSET + SFP_UPPER_PAGE_OFFSET
if not self.mst_pci_device:
return False
mlxreg_mngr = MlxregManager(self.mst_pci_device, self.slot_id, self.sdk_index)
dword = mlxreg_mngr.construct_dword(write_buffer)
return mlxreg_mngr.write_mlxreg_eeprom(num_bytes, dword, device_address, page)
@classmethod
def mgmt_phy_mod_pwr_attr_get(cls, power_attr_type, sdk_handle, sdk_index, slot_id):
sx_mgmt_phy_mod_pwr_attr_p = new_sx_mgmt_phy_mod_pwr_attr_t_p()

View File

@ -0,0 +1,55 @@
"""
module holding the correct values for the sfp_test.py
"""
read_eeprom_output = """
Sending access register...
Field Name | Data
===================================
status | 0x00000000
slot_index | 0x00000000
module | 0x00000001
l | 0x00000000
device_address | 0x000000a8
page_number | 0x00000000
i2c_device_address | 0x00000050
size | 0x00000010
bank_number | 0x00000000
dword[0] | 0x43414331
dword[1] | 0x31353332
dword[2] | 0x31503250
dword[3] | 0x41324d53
dword[4] | 0x00000000
dword[5] | 0x00000000
dword[6] | 0x00000000
dword[7] | 0x00000000
dword[8] | 0x00000000
dword[9] | 0x00000000
dword[10] | 0x00000000
dword[11] | 0x00000000
dword[12] | 0x00000000
dword[13] | 0x00000000
dword[14] | 0x00000000
dword[15] | 0x00000000
dword[16] | 0x00000000
dword[17] | 0x00000000
dword[18] | 0x00000000
dword[19] | 0x00000000
dword[20] | 0x00000000
dword[21] | 0x00000000
dword[22] | 0x00000000
dword[23] | 0x00000000
dword[24] | 0x00000000
dword[25] | 0x00000000
dword[26] | 0x00000000
dword[27] | 0x00000000
dword[28] | 0x00000000
dword[29] | 0x00000000
dword[30] | 0x00000000
dword[31] | 0x00000000
===================================
"""
y_cable_part_number = "CAC115321P2PA2MS"
write_eeprom_dword1 = "dword[0]=0x01020304"
write_eeprom_dword2 = "dword[0]=0x01020304,dword[1]=0x05060000"

View File

@ -27,6 +27,9 @@ sys.path.insert(0, modules_path)
from sonic_platform.sfp import SFP, SX_PORT_MODULE_STATUS_INITIALIZING, SX_PORT_MODULE_STATUS_PLUGGED, SX_PORT_MODULE_STATUS_UNPLUGGED, SX_PORT_MODULE_STATUS_PLUGGED_WITH_ERROR, SX_PORT_MODULE_STATUS_PLUGGED_DISABLED
from sonic_platform.chassis import Chassis
from sonic_platform.sfp import MlxregManager
from tests.input_platform import output_sfp
class TestSfp:
@mock.patch('sonic_platform.device_data.DeviceDataManager.get_linecard_count', mock.MagicMock(return_value=8))
@ -80,3 +83,29 @@ class TestSfp:
description = sfp.get_error_description()
assert description == expected_description
@mock.patch('sonic_platform.sfp.SFP.get_mst_pci_device', mock.MagicMock(return_value="pciconf"))
@mock.patch('sonic_platform.sfp.MlxregManager.write_mlxreg_eeprom', mock.MagicMock(return_value=True))
def test_sfp_write_eeprom(self):
mlxreg_mngr = MlxregManager("", 0, 0)
write_buffer = bytearray([1,2,3,4])
offset = 793
sfp = SFP(0)
sfp.write_eeprom(offset, 4, write_buffer)
MlxregManager.write_mlxreg_eeprom.assert_called_with(4, output_sfp.write_eeprom_dword1, 153, 5)
offset = 641
write_buffer = bytearray([1,2,3,4,5,6])
sfp.write_eeprom(offset, 6, write_buffer)
MlxregManager.write_mlxreg_eeprom.assert_called_with(6, output_sfp.write_eeprom_dword2, 129, 4)
@mock.patch('sonic_platform.sfp.SFP.get_mst_pci_device', mock.MagicMock(return_value="pciconf"))
@mock.patch('sonic_platform.sfp.MlxregManager.read_mlxred_eeprom', mock.MagicMock(return_value=output_sfp.read_eeprom_output))
def test_sfp_read_eeprom(self):
mlxreg_mngr = MlxregManager("", 0, 0)
offset = 644
sfp = SFP(0)
assert output_sfp.y_cable_part_number == sfp.read_eeprom(offset, 16).decode()
MlxregManager.read_mlxred_eeprom.assert_called_with(132, 4, 16)