Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
Signed-off-by: Dante Su <dante.su@broadcom.com>
  • Loading branch information
ds952811 committed Nov 18, 2021
1 parent 07e75bc commit 7345a11
Show file tree
Hide file tree
Showing 6 changed files with 439 additions and 2 deletions.
14 changes: 14 additions & 0 deletions sonic_platform_base/sfp_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ class SfpBase(device_base.DeviceBase):
# Device type definition. Note, this is a constant.
DEVICE_TYPE = "sfp"

# SFP port type. Please note this is the cage type rather than transceiver type.
SFP_PORT_TYPE_UNSPECIFIED = "UNSPECIFIED"
SFP_PORT_TYPE_SFP = "SFP"
SFP_PORT_TYPE_QSFP = "QSFP"
SFP_PORT_TYPE_QSFPDD = "QSFP_DD"

# Generic error types definition
SFP_STATUS_INITIALIZING = 'Initializing'
SFP_STATUS_OK = 'OK'
Expand Down Expand Up @@ -59,6 +65,14 @@ def __init__(self):
self._xcvr_api_factory = XcvrApiFactory(self.read_eeprom, self.write_eeprom)
self._xcvr_api = None

def get_port_type(self):
"""
Retrieves the port/cage type of this SFP
Returns:
A string, the port/cage type of this SFP
"""
return self.SFP_PORT_TYPE_UNSPECIFIED

def get_num_thermals(self):
"""
Retrieves the number of thermals available on this SFP
Expand Down
209 changes: 207 additions & 2 deletions sonic_platform_base/sonic_xcvr/api/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@
from ...fields import consts
from ..xcvr_api import XcvrApi

import ast
import logging
from ...codes.public.cmis import CmisCodes
from ...fields import consts
from ..xcvr_api import XcvrApi
from .cmisCDB import CmisCdbApi
from .cmisVDM import CmisVdmApi
import time

CMIS_CONFIG_STATE_ACCEPTED = 0x1
CMIS_DATAPATH_STATE_ACTIVATED = 0x4

SFP_ERROR_DESCRIPTION_CMIS_DATAPATH_ERROR = 'CMIS datapath error'
SFP_ERROR_DESCRIPTION_CMIS_CONFIG_ERROR = 'CMIS config error'
SFP_ERROR_DESCRIPTION_CMIS_NOT_READY = 'CMIS not ready'

logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

Expand Down Expand Up @@ -129,8 +138,8 @@ def get_transceiver_info(self):
"specification_compliance": admin_info[consts.MEDIA_TYPE_FIELD],
"vendor_date": admin_info[consts.VENDOR_DATE_FIELD],
"vendor_oui": admin_info[consts.VENDOR_OUI_FIELD],
# TODO
"application_advertisement": "N/A",
"application_advertisement": admin_info[consts.APPLS_ADVT_FIELD],
"memory_type": "flat" if self.is_flat_memory() else "paged"
}
xcvr_info['host_electrical_interface'] = self.get_host_electrical_interface()
xcvr_info['media_interface_code'] = self.get_module_media_interface()
Expand Down Expand Up @@ -589,6 +598,24 @@ def get_lpmode_support(self):
return False
return "Power Class 1" not in power_class

def get_lpmode(self):
if self.is_flat_memory() or not self.get_lpmode_support():
return False
return self.get_module_state() == CmisCodes.MODULE_STATE[1]

def set_lpmode(self, lpmode):
if self.is_flat_memory() or not self.get_lpmode_support():
return False
byte = self.xcvr_eeprom.read(consts.MODULE_LEVEL_CONTROL)
# Clear LowPwrAllowRequestHW (BIT6, CMIS v4 onwards)
byte &= 0xbf
# Set/Clear LowPwrRequestHW (BIT4, CMIS v3 onwards)
if lpmode:
byte |= 0x10
else:
byte &= 0xef
return self.xcvr_eeprom.write(consts.MODULE_LEVEL_CONTROL, byte)

def get_power_override_support(self):
return False

Expand Down Expand Up @@ -1665,4 +1692,182 @@ def get_transceiver_loopback(self):
trans_loopback['host_input_loopback_lane7'] = host_input_loopback_status[6]
trans_loopback['host_input_loopback_lane8'] = host_input_loopback_status[7]
return trans_loopback

def set_datapath_deinit(self, lanes, enable):
cmis_major = self.xcvr_eeprom.read(consts.CMIS_MAJOR_REVISION)
data = self.xcvr_eeprom.read(consts.DATAPATH_DEINIT_FIELD)
for lane in lanes:
lane &= self.NUM_CHANNELS - 1
if cmis_major >= 4: # CMIS v4 onwards
if enable:
data |= (1 << lane)
else:
data &= ~(1 << lane)
else: # CMIS v3
if enable:
data &= ~(1 << lane)
else:
data |= (1 << lane)
self.xcvr_eeprom.write(consts.DATAPATH_DEINIT_FIELD, data)

def get_host_speed(self, ifname):
# see host_electrical_interface of sff8024.py
speed = 0
if '400G' in ifname:
speed = 400000
elif '200G' in ifname:
speed = 200000
elif '100G' in ifname or 'CAUI-4' in ifname:
speed = 100000
elif '50G' in ifname or 'LAUI-2' in ifname:
speed = 50000
elif '40G' in ifname or 'XLAUI' in ifname or 'XLPPI' in ifname:
speed = 40000
elif '25G' in ifname:
speed = 25000
elif '10G' in ifname or 'SFI' in ifname or 'XFI' in ifname:
speed = 10000
elif '1000BASE' in ifname:
speed = 1000
return speed

def get_cmis_state(self):
state = {
'module_state': self.get_module_state(),
'config_state': self.get_config_datapath_hostlane_status(),
'datapath_state': self.get_datapath_state()
}
return state

def get_cmis_application_selected(self, lane):
ap_code = 0
if not self.is_flat_memory():
field = "{}_{}".format(consts.STAGED_CTRL0_APSEL_FIELD, lane + 1)
ap_code = self.xcvr_eeprom.read(field) >> 4

return (ap_code & 0xf)

def get_cmis_application_matched(self, host_speed, host_lanes):
if host_speed == 0 or len(host_lanes) == 0:
return 0

ap_code = 0
appl_dict = ast.literal_eval(self.xcvr_eeprom.read(consts.APPLS_ADVT_FIELD))
for c in appl_dict.keys():
d = appl_dict[c]
if d.get('host_lane_count') != len(host_lanes):
continue
if self.get_host_speed(d.get('host_electrical_interface_id')) != host_speed:
continue
ap_code = c

return (ap_code & 0xf)

def get_cmis_application_update(self, host_lanes, host_speed):
"""
Check for CMIS application update and the new application code
Args:
host_speed:
Integer, the port speed of the host interface
host_lanes:
Integer List, a list of the 0-based lane indexes of the host interface
Returns:
(updated, appl_code)
"""
if self.is_flat_memory():
return (False, 1)

# DEBUG
#self.reset_module(True)
#time.sleep(3)

app_new = self.get_cmis_application_matched(host_speed, host_lanes)
if app_new != 1 or len(host_lanes) != self.NUM_CHANNELS:
logger.info("Non-default application is not supported")
return (False, 1)

app_old = self.get_cmis_application_selected(0)
if app_old == app_new:
skip = True
dp_state = self.get_datapath_state()
conf_state = self.get_config_datapath_hostlane_status()
for lane in host_lanes:
lane &= self.NUM_CHANNELS - 1
name = "DP{}State".format(lane + 1)
if dp_state[name] != CmisCodes.DATAPATH_STATE[4]:
skip = False
break
name = "ConfigStatusLane{}".format(lane + 1)
if conf_state[name] != CmisCodes.CONFIG_STATUS[1]:
skip = False
break
if skip:
return (False, app_old)

return (True, app_new)

def set_cmis_application_stop(self, host_lanes):
"""
(Stage 1) Data Path Deinitialization Tx power disable
"""
# D.2.2 Software Deinitialization
self.set_datapath_deinit(host_lanes, True)
self.set_lpmode(True)

# D.1.3 Software Configuration and Initialization
self.xcvr_eeprom.write(consts.TX_DISABLE_FIELD, 0xff)
self.set_lpmode(False)
return True

def set_cmis_application_apsel(self, host_lanes, appl_code):
"""
(Stage 2) Update the application selection
"""
# Update the application selection
mask = 0
for lane in host_lanes:
addr = "{}_{}".format(consts.STAGED_CTRL0_APSEL_FIELD, lane + 1)
data = appl_code << 4
self.xcvr_eeprom.write(addr, data)
mask |= (1 << lane)

# Apply DataPathInit
return self.xcvr_eeprom.write(consts.STAGED_CTRL0_APPLY_DPINIT_FIELD, mask)

def set_cmis_application_start(self, host_lanes):
"""
(Stage 3) Initialize the new data path
"""
return self.set_datapath_deinit(host_lanes, False)

def set_cmis_application_txon(self, host_lanes):
"""
(Stage 4) Initialize the new data path
"""
mask = 0
for lane in host_lanes:
mask |= 1 << (lane & (self.NUM_CHANNELS - 1))
mask = ~mask & 0xff
return self.xcvr_eeprom.write(consts.TX_DISABLE_FIELD, mask)

def get_error_description(self):
dp_state = self.get_datapath_state()
conf_state = self.get_config_datapath_hostlane_status()
for lane in range(self.NUM_CHANNELS):
name = "DP{}State".format(lane + 1)
if dp_state[name] != CmisCodes.DATAPATH_STATE[4]:
return dp_state[name]

name = "ConfigStatusLane{}".format(lane + 1)
if conf_state[name] != CmisCodes.CONFIG_STATUS[1]:
return conf_state[name]

state = self.get_module_state()
if state != CmisCodes.MODULE_STATE[3]:
return state

return None

# TODO: other XcvrApi methods
7 changes: 7 additions & 0 deletions sonic_platform_base/sonic_xcvr/fields/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,18 +262,25 @@
TRANS_CONFIG_FIELD = "TransceriverConfig"
MODULE_LEVEL_CONTROL = "ModuleControl"

APPLS_ADVT_FIELD = "Applications Advertisement"
CTRLS_ADVT_FIELD = "Supported Controls Advertisement"
FLAGS_ADVT_FIELD = "Supported Flags Advertisement"

LANE_DATAPATH_CTRL_FIELD = "Lane Control and Data Path Control"
LANE_DATAPATH_STATUS_FIELD = "Lane Status and Data Path Status"
DATAPATH_DEINIT_FIELD = "Data Path Deinit"
LEN_MULT_FIELD = "LengthMultiplier"
MAX_POWER_FIELD = "MaxPower"
MGMT_CHAR_FIELD = "Management Characteristics"
MGMT_CHAR_MISC_FIELD = "Management Characteristics (Misc)"

MODULE_CHAR_ADVT_FIELD = "Module Characteristics Advertising"

STAGED_CTRL0_FIELD = "Staged Control Set 0"
STAGED_CTRL0_APPLY_DPINIT_FIELD = "Staged Control Set 0 Apply DataPathInit"
STAGED_CTRL0_APPLY_IMMEDIATE_FIELD = "Staged Control Set 0 Apply Immediate"
STAGED_CTRL0_APSEL_FIELD = "Staged Control Set 0 ApSel"

# C-CMIS

# Module configuration support fields
Expand Down
68 changes: 68 additions & 0 deletions sonic_platform_base/sonic_xcvr/fields/public/cmis.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from ..xcvr_field import NumberRegField
from ..xcvr_field import RegField
from .. import consts

import sonic_platform_base.sonic_sfp.sff8024 as sff8024

class CableLenField(NumberRegField):
def __init__(self, name, offset, *fields, **kwargs):
kwargs["deps"] = [consts.LEN_MULT_FIELD]
Expand All @@ -11,3 +14,68 @@ def decode(self, raw_data, **decoded_deps):
len_mult = decoded_deps.get(consts.LEN_MULT_FIELD)
mult = 10 ** (len_mult - 1)
return base_len * mult

class ApplicationAdvertField(RegField):
"""
Interprets application advertising bytes as a string
"""
def __init__(self, name, offset, *fields, **kwargs):
super(ApplicationAdvertField, self).__init__(name, offset, *fields, **kwargs)
self.size = kwargs.get("size")

def decode(self, raw_data, **decoded_deps):
host_if_dict = sff8024.host_electrical_interface
media_if_dict = None

# Decode media type
code = "{:02x}".format(raw_data[0])
if code not in sff8024.type_of_media_interface.keys():
return None

# Find the host/media dictionary
type = sff8024.type_of_media_interface[code]
if type == "nm_850_media_interface":
media_if_dict = sff8024.nm_850_media_interface
elif type == "sm_media_interface":
media_if_dict = sff8024.sm_media_interface
elif type == "passive_copper_media_interface":
media_if_dict = sff8024.passive_copper_media_interface
elif type == "active_cable_media_interface":
media_if_dict = sff8024.active_cable_media_interface
elif type == "base_t_media_interface":
media_if_dict = sff8024.base_t_media_interface
if media_if_dict is None:
return None

idx = 1
pos = 1
dat = {}
while pos < self.size:
appl = { }

code = "{:02x}".format(raw_data[pos+0])
if code in ['00', 'ff']:
break
if code in host_if_dict:
appl['host_electrical_interface_id'] = host_if_dict[code]
else:
appl['host_electrical_interface_id'] = 'Unknown'

code = "{:02x}".format(raw_data[pos+1])
if code in ['00', 'ff']:
break
if code in media_if_dict:
appl['module_media_interface_id'] = media_if_dict[code]
else:
appl['module_media_interface_id'] = 'Unknown'

appl['host_lane_count'] = raw_data[pos+2] >> 4
appl['media_lane_count'] = raw_data[pos+2] & 0xf
appl['host_lane_assignment_options'] = raw_data[pos+3]
appl['media_lane_assignment_options'] = None

dat[idx] = appl
idx += 1
pos += 4

return str(dat)
Loading

0 comments on commit 7345a11

Please sign in to comment.