From cd692122cd3a7eaf0420121559f48aa937d37a8c Mon Sep 17 00:00:00 2001 From: Aravind Mani <53524901+aravindmani-1@users.noreply.github.com> Date: Mon, 29 Nov 2021 12:02:02 +0530 Subject: [PATCH] [SFP-Refactor] Implement CMIS Low Power mode (#237) * [SFP-Refactor] Implement CMIS LP mode --- .../sonic_xcvr/api/public/c_cmis.py | 4 +-- .../sonic_xcvr/api/public/cmis.py | 36 ++++++++++++++++--- .../sonic_xcvr/fields/consts.py | 2 +- .../sonic_xcvr/sfp_optoe_base.py | 18 ++++++++++ tests/sonic_xcvr/test_ccmis.py | 4 +++ tests/sonic_xcvr/test_cmis.py | 8 +++-- 6 files changed, 62 insertions(+), 10 deletions(-) diff --git a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py index bcf5c2683b31..9330e002e917 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/c_cmis.py @@ -122,11 +122,11 @@ def set_laser_freq(self, freq): assert channel_number % 3 == 0 if channel_number > hi_ch_num or channel_number < low_ch_num: raise ValueError('Provisioned frequency out of range. Max Freq: 196100; Min Freq: 191300 GHz.') - self.set_low_power(True) + self.set_lpmode(True) time.sleep(5) status = self.xcvr_eeprom.write(consts.LASER_CONFIG_CHANNEL, channel_number) time.sleep(1) - self.set_low_power(False) + self.set_lpmode(False) time.sleep(1) return status diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 16ea4adafd14..e8cff76b2a8b 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -871,15 +871,41 @@ def reset_module(self, reset = False): else: return True - def set_low_power(self, AssertLowPower): + def get_lpmode(self): + ''' + Retrieves Low power module status + Returns True if module in low power else returns False. + ''' + if self.is_flat_memory() or not self.get_lpmode_support(): + return False + + lpmode = self.xcvr_eeprom.read(consts.TRANS_MODULE_STATUS_FIELD) + if lpmode is not None: + if lpmode.get('ModuleState') == 'ModuleLowPwr': + return True + return False + + def set_lpmode(self, lpmode): ''' This function sets the module to low power state. - AssertLowPower being 0 means "set to high power" - AssertLowPower being 1 means "set to low power" + lpmode being False means "set to high power" + lpmode being True means "set to low power" Return True if the provision succeeds, False if it fails ''' - low_power_control = AssertLowPower << 6 - return self.xcvr_eeprom.write(consts.MODULE_LEVEL_CONTROL, low_power_control) + + if self.is_flat_memory() or not self.get_lpmode_support(): + return False + + lpmode_val = self.xcvr_eeprom.read(consts.MODULE_LEVEL_CONTROL) + if lpmode_val is not None: + if lpmode is True: + lpmode_val = lpmode_val | (1 << 4) + else: + lpmode_val = lpmode_val & ~(1 << 4) + self.xcvr_eeprom.write(consts.MODULE_LEVEL_CONTROL, lpmode_val) + time.sleep(0.1) + return self.get_lpmode() + return False def get_loopback_capability(self): ''' diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index c310d56009f9..4cfdd3824b9e 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -260,7 +260,7 @@ LASER_TUNING_DETAIL = "TxTuningDetail" # CONFIG -TRANS_CONFIG_FIELD = "TransceriverConfig" +TRANS_CONFIG_FIELD = "TransceiverConfig" MODULE_LEVEL_CONTROL = "ModuleControl" CTRLS_ADVT_FIELD = "Supported Controls Advertisement" diff --git a/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py b/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py index b4a77765029b..8f6095adc855 100644 --- a/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py +++ b/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py @@ -128,6 +128,24 @@ def set_power_override(self, power_override, power_set): def get_eeprom_path(self): raise NotImplementedError + def get_lpmode(self): + """ + This common API is applicable only for CMIS as Low Power mode can be verified + using EEPROM registers.For other media types like QSFP28/QSFP+ etc., platform + vendors has to implement accordingly. + """ + api = self.get_xcvr_api() + return api.get_lpmode() if api is not None else None + + def set_lpmode(self, lpmode): + """ + This common API is applicable only for CMIS as Low Power mode can be controlled + via EEPROM registers.For other media types like QSFP28/QSFP+ etc., platform + vendors has to implement accordingly. + """ + api = self.get_xcvr_api() + return api.set_lp_mode(lpmode) if api is not None else None + def read_eeprom(self, offset, num_bytes): try: with open(self.get_eeprom_path(), mode='rb', buffering=0) as f: diff --git a/tests/sonic_xcvr/test_ccmis.py b/tests/sonic_xcvr/test_ccmis.py index ac899f20c9d0..99a67b62b7dc 100644 --- a/tests/sonic_xcvr/test_ccmis.py +++ b/tests/sonic_xcvr/test_ccmis.py @@ -97,6 +97,10 @@ def test_get_supported_freq_config(self, mock_response, expected): (195950, (0xff, -72, 120, 191300, 196100)), ]) def test_set_laser_freq(self, input_param, mock_response): + self.api.is_flat_memory = MagicMock() + self.api.is_flat_memory.return_value = False + self.api.get_lpmode_support = MagicMock() + self.api.get_lpmode_support.return_value = False self.api.get_supported_freq_config = MagicMock() self.api.get_supported_freq_config.return_value = mock_response self.api.set_laser_freq(input_param) diff --git a/tests/sonic_xcvr/test_cmis.py b/tests/sonic_xcvr/test_cmis.py index c685e70cbfde..fd788c3582ad 100644 --- a/tests/sonic_xcvr/test_cmis.py +++ b/tests/sonic_xcvr/test_cmis.py @@ -747,8 +747,12 @@ def test_get_supported_power_config(self, mock_response, expected): def test_reset_module(self): self.api.reset_module(True) - def test_set_low_power(self, ): - self.api.set_low_power(True) + def test_set_low_power(self): + self.api.is_flat_memory = MagicMock() + self.api.is_flat_memory.return_value = False + self.api.get_lpmode_support = MagicMock() + self.api.get_lpmode_support.return_value = False + self.api.set_lpmode(True) @pytest.mark.parametrize("mock_response, expected", [ (127,