Skip to content

Commit

Permalink
DellEMC: Z9332f change SFP detection logic (sonic-net#6261)
Browse files Browse the repository at this point in the history
- Dynamically change EEPROM driver based on media type.
- Otherwise, EEPROM INFO and DOM INFO might not be fetched properly and will result in erroneous output.
  • Loading branch information
aravindmani-1 authored and lguohan committed Jan 9, 2021
1 parent c6f14c9 commit 7305b55
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ switch_board_qsfp() {
"new_device")
for ((i=10;i<=41;i++));
do
echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-$i/$1
echo optoe3 0x50 > /sys/bus/i2c/devices/i2c-$i/$1
done
;;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python

#############################################################################
# DELLEMC S5248F
# DELLEMC Z9332F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
Expand All @@ -11,6 +11,7 @@
try:
import os
import time
import subprocess
import struct
import mmap
from sonic_platform_base.sfp_base import SfpBase
Expand Down Expand Up @@ -157,19 +158,76 @@
'vendor_date': [SFP_INFO_OFFSET, 84, 8, 'parse_vendor_date'],
'ModuleThreshold': [SFP_DOM_OFFSET, 0, 56, 'parse_alarm_warning_threshold'],
}
MEDIA_TYPE_OFFSET = 0
MEDIA_TYPE_WIDTH = 1

SFP_TYPE_LIST = [
'03' # SFP/SFP+/SFP28 and later
]
QSFP_TYPE_LIST = [
'0c', # QSFP
'0d', # QSFP+ or later
'11' # QSFP28 or later
]
QSFP_DD_TYPE_LIST = [
'18' #QSFP-DD Type
]
OSFP_TYPE_LIST=[
'19' # OSFP 8X Type
]



class Sfp(SfpBase):
"""
DELLEMC Platform-specific Sfp class
"""
BASE_RES_PATH = "/sys/bus/pci/devices/0000:09:00.0/resource0"
_port_to_i2c_mapping = {
1: 10,
2: 11,
3: 12,
4: 13,
5: 14,
6: 15,
7: 16,
8: 17,
9: 18,
10: 19,
11: 20,
12: 21,
13: 22,
14: 23,
15: 24,
16: 25,
17: 26,
18: 27,
19: 28,
20: 29,
21: 30,
22: 31,
23: 32,
24: 33,
25: 34,
26: 35,
27: 36,
28: 37,
29: 38,
30: 39,
31: 40,
32: 41,
33: 1,
34: 2
}

def __init__(self, index, sfp_type, eeprom_path):
SfpBase.__init__(self)
self.sfp_type = sfp_type
self.index = index
self.eeprom_path = eeprom_path
#sfp_type is the native port type and media_type is the transceiver type
#media_type will be detected in get_transceiver_info
self.sfp_type = sfp_type
self.media_type = self.sfp_type
self.qsfpInfo = sff8436InterfaceId()
self.qsfpDomInfo = sff8436Dom()
self.sfpInfo = sff8472InterfaceId()
Expand Down Expand Up @@ -238,7 +296,7 @@ def _get_eeprom_data(self, eeprom_key):
eeprom_data = None
page_offset = None

if(self.sfp_type == 'QSFP'):
if(self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
page_offset = sff8436_parser[eeprom_key][PAGE_OFFSET]
eeprom_data_raw = self._read_eeprom_bytes(
self.eeprom_path,
Expand Down Expand Up @@ -292,6 +350,9 @@ def get_transceiver_info(self):
transceiver_info_dict = {}
compliance_code_dict = {}
transceiver_info_dict = dict.fromkeys(info_dict_keys, 'N/A')
self.media_type = self.set_media_type()
self.reinit_sfp_driver()

# BaseInformation
try:
iface_data = self._get_eeprom_data('type')
Expand All @@ -301,7 +362,7 @@ def get_transceiver_info(self):
rate_identifier = iface_data['data']['RateIdentifier']['value']
identifier = iface_data['data']['type']['value']
type_abbrv_name=iface_data['data']['type_abbrv_name']['value']
if(self.sfp_type == 'QSFP'):
if(self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
bit_rate = str(
iface_data['data']['Nominal Bit Rate(100Mbs)']['value'])
for key in qsfp_compliance_code_tup:
Expand Down Expand Up @@ -398,7 +459,7 @@ def get_transceiver_threshold_info(self):
try:
# Module Threshold
module_threshold_data = self._get_eeprom_data('ModuleThreshold')
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
transceiver_dom_threshold_dict['temphighalarm'] = module_threshold_data['data']['TempHighAlarm']['value']
transceiver_dom_threshold_dict['temphighwarning'] = module_threshold_data['data']['TempHighWarning']['value']
transceiver_dom_threshold_dict['templowalarm'] = module_threshold_data['data']['TempLowAlarm']['value']
Expand Down Expand Up @@ -431,7 +492,7 @@ def get_transceiver_threshold_info(self):
except (ValueError, TypeError) : pass

try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
channel_threshold_data = self._get_eeprom_data('ChannelThreshold')
transceiver_dom_threshold_dict['rxpowerhighalarm'] = channel_threshold_data['data']['RxPowerHighAlarm']['value']
transceiver_dom_threshold_dict['rxpowerhighwarning'] = channel_threshold_data['data']['RxPowerHighWarning']['value']
Expand Down Expand Up @@ -598,7 +659,7 @@ def get_rx_los(self):
"""
rx_los = False
try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
rx_los_data = self._get_eeprom_data('rx_los')
# As the function expects a single boolean, if any one channel experience LOS,
# is considered LOS for QSFP
Expand All @@ -618,7 +679,7 @@ def get_tx_fault(self):
"""
tx_fault = False
try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
tx_fault_data = self._get_eeprom_data('tx_fault')
for tx_fault_id in ('Tx1Fault', 'Tx2Fault', 'Tx3Fault', 'Tx4Fault') :
tx_fault |= (tx_fault_data['data'][tx_fault_id]['value'] is 'On')
Expand All @@ -636,7 +697,7 @@ def get_tx_disable(self):
"""
tx_disable = False
try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
tx_disable |= (tx_disable_data['data'][tx_disable_id]['value'] is 'On')
Expand All @@ -656,7 +717,7 @@ def get_tx_disable_channel(self):
"""
tx_disable_channel = 0
try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
tx_disable_data = self._get_eeprom_data('tx_disable')
for tx_disable_id in ('Tx1Disable', 'Tx2Disable', 'Tx3Disable', 'Tx4Disable'):
tx_disable_channel <<= 1
Expand All @@ -671,7 +732,7 @@ def get_lpmode(self):
"""
lpmode_state = False
try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)

Expand All @@ -692,7 +753,7 @@ def get_power_override(self):
power_override_state = False

try:
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
power_override_data = self._get_eeprom_data('power_override')
power_override = power_override_data['data']['PowerOverRide']['value']
power_override_state = (power_override is 'On')
Expand Down Expand Up @@ -730,7 +791,7 @@ def get_tx_bias(self):
tx_bias_list = []
try:
tx_bias_data = self._get_eeprom_data('ChannelMonitor')
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
for tx_bias_id in ('TX1Bias', 'TX2Bias', 'TX3Bias', 'TX4Bias') :
tx_bias = tx_bias_data['data'][tx_bias_id]['value']
tx_bias_list.append(tx_bias)
Expand All @@ -748,7 +809,7 @@ def get_rx_power(self):
rx_power_list = []
try:
rx_power_data = self._get_eeprom_data('ChannelMonitor')
if (self.sfp_type == 'QSFP'):
if (self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
for rx_power_id in ('RX1Power', 'RX2Power', 'RX3Power', 'RX4Power'):
rx_power = rx_power_data['data'][rx_power_id]['value']
rx_power_list.append(rx_power)
Expand All @@ -765,7 +826,7 @@ def get_tx_power(self):
"""
tx_power_list = []
try:
if(self.sfp_type == 'QSFP'):
if(self.media_type == 'QSFP' or self.media_type == 'QSFP-DD'):
# QSFP capability byte parse, through this byte can know whether it support tx_power or not.
# TODO: in the future when decided to migrate to support SFF-8636 instead of SFF-8436,
# need to add more code for determining the capability and version compliance
Expand Down Expand Up @@ -895,6 +956,12 @@ def get_status(self):
reset = self.get_reset_status()
return (not reset)

def get_port_form_factor(self):
"""
Retrieves the native port type
"""
return self.sfp_type

def get_max_port_power(self):
"""
Retrieves the maximum power allowed on the port in watts
Expand All @@ -906,3 +973,58 @@ def get_max_port_power(self):
"""
return (12.0 if self.sfp_type=='QSFP' else 2.5)

def set_media_type(self):
"""
Reads optic eeprom byte to determine media type inserted
"""
eeprom_raw = []
eeprom_raw = self._read_eeprom_bytes(self.eeprom_path, MEDIA_TYPE_OFFSET, MEDIA_TYPE_WIDTH)
if eeprom_raw is not None:
if eeprom_raw[0] in SFP_TYPE_LIST:
self.media_type = 'SFP'
elif eeprom_raw[0] in QSFP_TYPE_LIST:
self.media_type = 'QSFP'
elif eeprom_raw[0] in QSFP_DD_TYPE_LIST:
self.media_type = 'QSFP-DD'
else:
#Set native port type if EEPROM type is not recognized/readable
self.media_type = self.sfp_type
else:
self.media_type = self.sfp_type

return self.media_type

def reinit_sfp_driver(self):
"""
Changes the driver based on media type detected
"""
del_sfp_path = "/sys/class/i2c-adapter/i2c-{0}/delete_device".format(self._port_to_i2c_mapping[self.index])
new_sfp_path = "/sys/class/i2c-adapter/i2c-{0}/new_device".format(self._port_to_i2c_mapping[self.index])
driver_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/name".format(self._port_to_i2c_mapping[self.index])
delete_device = "echo 0x50 >" + del_sfp_path

try:
with os.fdopen(os.open(driver_path, os.O_RDONLY)) as fd:
driver_name = fd.read()
driver_name = driver_name.rstrip('\r\n')
driver_name = driver_name.lstrip(" ")

#Avoid re-initialization of the QSFP/SFP optic on QSFP/SFP port.
if (self.media_type == 'SFP' and (driver_name == 'optoe1' or driver_name == 'optoe3')):
subprocess.Popen(delete_device, shell=True, stdout=subprocess.PIPE)
new_device = "echo optoe2 0x50 >" + new_sfp_path
subprocess.Popen(new_device, shell=True, stdout=subprocess.PIPE)
time.sleep(2)
elif (self.media_type == 'QSFP' and (driver_name == 'optoe2' or driver_name == 'optoe3')):
subprocess.Popen(delete_device, shell=True, stdout=subprocess.PIPE)
new_device = "echo optoe1 0x50 >" + new_sfp_path
subprocess.Popen(new_device, shell=True, stdout=subprocess.PIPE)
time.sleep(2)
elif (self.media_type == 'QSFP-DD' and (driver_name == 'optoe1' or driver_name == 'optoe2')):
subprocess.Popen(delete_device, shell=True, stdout=subprocess.PIPE)
new_device = "echo optoe3 0x50 >" + new_sfp_path
subprocess.Popen(new_device, shell=True, stdout=subprocess.PIPE)
time.sleep(2)

except IOError as e:
print("Error: Unable to open file: %s" % str(e))

0 comments on commit 7305b55

Please sign in to comment.