diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8636.py b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py index 6d138e01d..00473bc7c 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/sff8636.py +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8636.py @@ -4,6 +4,7 @@ Implementation of XcvrApi that corresponds to the SFF-8636 specification for QSFP28 pluggable transceivers. """ +import re from ...fields import consts from ..xcvr_api import XcvrApi @@ -12,6 +13,7 @@ class Sff8636Api(XcvrApi): NUM_CHANNELS = 4 + POWER_CLASS_PATTERN = r'^Power Class ([1-8])' def __init__(self, xcvr_eeprom): super(Sff8636Api, self).__init__(xcvr_eeprom) @@ -403,3 +405,44 @@ def get_lpmode(self): # Since typically optics come up by default set to high power, in this case, # power_override not being set, function will return high power mode. return power_set and power_override + + def get_power_class(self): + ''' + Retrieves power class of the module. + + Returns: + int: Power class of the module, None if it fails + ''' + power_class_str = self.xcvr_eeprom.read(consts.POWER_CLASS_FIELD) + power_class = None + + if power_class_str is None: + return power_class + + pattern = re.compile(self.POWER_CLASS_PATTERN) + match = pattern.match(power_class_str) + if match: + power_class = int(match.group(1)) + + return power_class + + def set_high_power_class(self, power_class, enable): + ''' + This function sets high power class for the module. + It is only applicable for power class >= 5. + + Args: + power_class (int): Power class to be enabled/disabled + enable (bool): True to enable high power class, False to disable + + Returns: + bool: True if the provision succeeds, False if it fails + ''' + ret = True + + if power_class >= 8: + ret = self.xcvr_eeprom.write(consts.HIGH_POWER_CLASS_ENABLE_CLASS_8, enable) + elif power_class >= 5: + ret = self.xcvr_eeprom.write(consts.HIGH_POWER_CLASS_ENABLE_CLASS_5_TO_7, enable) + + return ret diff --git a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py index c49bf64f9..15ddf7380 100644 --- a/sonic_platform_base/sonic_xcvr/api/xcvr_api.py +++ b/sonic_platform_base/sonic_xcvr/api/xcvr_api.py @@ -637,3 +637,24 @@ def get_error_description(self): """ raise NotImplementedError + def get_power_class(self): + """ + Retrieves the power class of the module + + Returns: + int: Power class of the module, None if it fails + """ + raise NotImplementedError + + def set_high_power_class(self, power_class, enable): + """ + This function sets high power class for the module. + + Args: + power_class (int): Power class to enable/disable + enable (bool): True to enable high power class, False to disable + + Returns: + bool: True if the provision succeeds, False if it fails + """ + raise NotImplementedError diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index cca473b9a..d8f292c3a 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -104,6 +104,8 @@ POWER_CTRL_FIELD = "Power Control" POWER_OVERRIDE_FIELD = "Power Override" POWER_SET_FIELD = "Power Set" +HIGH_POWER_CLASS_ENABLE_CLASS_5_TO_7 = "High Power Class Enable (Class 5-7)" +HIGH_POWER_CLASS_ENABLE_CLASS_8 = "High Power Class Enable (Class 8)" # SFF-8636 diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8636.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8636.py index 2a9032877..f25e61116 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8636.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8636.py @@ -128,6 +128,8 @@ def __init__(self, codes): self.POWER_CTRL = NumberRegField(consts.POWER_CTRL_FIELD, self.get_addr(0, 93), RegBitField(consts.POWER_OVERRIDE_FIELD, 0, ro=False), RegBitField(consts.POWER_SET_FIELD, 1, ro=False), + RegBitField(consts.HIGH_POWER_CLASS_ENABLE_CLASS_5_TO_7, 2, ro=False), + RegBitField(consts.HIGH_POWER_CLASS_ENABLE_CLASS_8, 3, ro=False), ro=False ) diff --git a/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py b/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py index a1933d1e9..cb2629d89 100644 --- a/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py +++ b/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py @@ -280,3 +280,27 @@ def get_error_description(self): """ api = self.get_xcvr_api() return api.get_error_description() if api is not None else None + + def get_power_class(self): + """ + Get the power class of the module + + Returns: + Integer that represents the power class of the module, None if it fails + """ + api = self.get_xcvr_api() + return api.get_power_class() if api is not None else None + + def set_high_power_class(self, power_class, enable): + """ + Set the high power class of the module + + Args: + power_class: Integer that represents the power class to enable or disable + enable: Boolean that represents whether to enable or disable the high power class + + Returns: + Boolean, True if successful, False if not + """ + api = self.get_xcvr_api() + return api.set_high_power_class(power_class, enable) if api is not None else False diff --git a/tests/sonic_xcvr/test_sff8636.py b/tests/sonic_xcvr/test_sff8636.py index 7a566bc2d..860694b5e 100644 --- a/tests/sonic_xcvr/test_sff8636.py +++ b/tests/sonic_xcvr/test_sff8636.py @@ -156,6 +156,41 @@ def test_set_lpmode(self): self.api.get_power_override_support.return_value = False assert not self.api.set_lpmode(True) + def test_set_high_power_class(self): + with patch.object(self.api, 'xcvr_eeprom'): + # Test low power class + assert self.api.set_high_power_class(1, True) + + # Test high power class 5-7 + assert self.api.set_high_power_class(5, True) + + # Test high power class 8 + assert self.api.set_high_power_class(8, True) + + # Test high power class disable + assert self.api.set_high_power_class(8, False) + + def test_get_power_class(self): + with patch.object(self.api, 'xcvr_eeprom') as mock_eeprom: + mock_eeprom.read = MagicMock() + + mock_eeprom.read.return_value = "Power Class 1 Module (1.5W max.)" + assert self.api.get_power_class() == 1 + + # Invalid power class + mock_eeprom.read.return_value = "Power Class 9 Module (555.5W max.)" + assert self.api.get_power_class() is None + + # Invalid power class string + mock_eeprom.read.return_value = "XXX Power Class 1 Module (1.5W max.)" + assert self.api.get_power_class() is None + + mock_eeprom.read.return_value = "XYZ" + assert self.api.get_power_class() is None + + mock_eeprom.read.return_value = None + assert self.api.get_power_class() is None + @pytest.mark.parametrize("mock_response, expected",[ ( [