|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +############################################################################# |
| 4 | +# Celestica |
| 5 | +# |
| 6 | +# Module contains an implementation of SONiC Platform Base API and |
| 7 | +# provides the sfp status which are available in the platform |
| 8 | +# |
| 9 | +############################################################################# |
| 10 | + |
| 11 | +import time |
| 12 | + |
| 13 | +try: |
| 14 | + from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase |
| 15 | + from .helper import APIHelper |
| 16 | +except ImportError as e: |
| 17 | + raise ImportError(str(e) + "- required module not found") |
| 18 | + |
| 19 | + |
| 20 | +SFP_I2C_START = 26 |
| 21 | +I2C_EEPROM_PATH = '/sys/bus/i2c/devices/i2c-{0}/{0}-0050/eeprom' |
| 22 | + |
| 23 | + |
| 24 | +class SfpOptoe(SfpOptoeBase): |
| 25 | + """Platform-specific SfpOptoe class""" |
| 26 | + |
| 27 | + RESET_PATH = "/sys/devices/platform/dx010_cpld/qsfp_reset" |
| 28 | + LP_PATH = "/sys/devices/platform/dx010_cpld/qsfp_lpmode" |
| 29 | + PRS_PATH = "/sys/devices/platform/dx010_cpld/qsfp_modprs" |
| 30 | + |
| 31 | + def __init__(self, sfp_index=0, sfp_name=None): |
| 32 | + SfpOptoeBase.__init__(self) |
| 33 | + |
| 34 | + self._index = sfp_index |
| 35 | + self._port_num = self._index + 1 |
| 36 | + self._api_helper = APIHelper() |
| 37 | + self._name = sfp_name |
| 38 | + self._sfp_type = None |
| 39 | + |
| 40 | + def _detect_sfp_type(self): |
| 41 | + sfp_type = 'N/A' |
| 42 | + info = self.get_transceiver_info() |
| 43 | + if info: |
| 44 | + sfp_type = info.get("type_abbrv_name") |
| 45 | + # XXX: Need this hack until xcvrd is refactored |
| 46 | + if sfp_type in ["OSFP-8X", "QSFP-DD"]: |
| 47 | + sfp_type = "QSFP_DD" |
| 48 | + return sfp_type |
| 49 | + |
| 50 | + @property |
| 51 | + def sfp_type(self): |
| 52 | + if self._sfp_type is None: |
| 53 | + self._sfp_type = self._detect_sfp_type() |
| 54 | + return self._sfp_type |
| 55 | + |
| 56 | + def get_eeprom_path(self): |
| 57 | + port_to_i2c_mapping = SFP_I2C_START + self._index |
| 58 | + port_eeprom_path = I2C_EEPROM_PATH.format(port_to_i2c_mapping) |
| 59 | + return port_eeprom_path |
| 60 | + |
| 61 | + def get_name(self): |
| 62 | + """ |
| 63 | + Retrieves the name of the device |
| 64 | + Returns: |
| 65 | + string: The name of the device |
| 66 | + """ |
| 67 | + return self._name |
| 68 | + |
| 69 | + def get_status(self): |
| 70 | + """ |
| 71 | + Retrieves the operational status of the device |
| 72 | + Returns: |
| 73 | + A boolean value, True if device is operating properly, False if not |
| 74 | + """ |
| 75 | + if not self.get_presence(): |
| 76 | + return False |
| 77 | + reset = self.get_reset_status() |
| 78 | + if reset: |
| 79 | + status = False |
| 80 | + else: |
| 81 | + status = True |
| 82 | + return status |
| 83 | + |
| 84 | + def get_reset_status(self): |
| 85 | + """ |
| 86 | + Retrieves the reset status of SFP |
| 87 | + Returns: |
| 88 | + A Boolean, True if reset enabled, False if disabled |
| 89 | + """ |
| 90 | + reset_status_raw = self._api_helper.read_txt_file( |
| 91 | + self.RESET_PATH).rstrip() |
| 92 | + if not reset_status_raw: |
| 93 | + return False |
| 94 | + |
| 95 | + reg_value = int(reset_status_raw, 16) |
| 96 | + bin_format = bin(reg_value)[2:].zfill(32) |
| 97 | + return bin_format[::-1][self._index] == '0' |
| 98 | + |
| 99 | + def get_presence(self): |
| 100 | + """ |
| 101 | + Retrieves the presence of the PSU |
| 102 | + Returns: |
| 103 | + bool: True if PSU is present, False if not |
| 104 | + """ |
| 105 | + presence_status_raw = self._api_helper.read_txt_file( |
| 106 | + self.PRS_PATH).rstrip() |
| 107 | + if not presence_status_raw: |
| 108 | + return False |
| 109 | + |
| 110 | + content = presence_status_raw.rstrip() |
| 111 | + reg_value = int(content, 16) |
| 112 | + |
| 113 | + # Determind if port_num start from 1 or 0 |
| 114 | + bit_index = self._index |
| 115 | + |
| 116 | + # Mask off the bit corresponding to our port |
| 117 | + mask = (1 << bit_index) |
| 118 | + |
| 119 | + # ModPrsL is active low |
| 120 | + if reg_value & mask == 0: |
| 121 | + return True |
| 122 | + |
| 123 | + return False |
| 124 | + |
| 125 | + def get_lpmode(self): |
| 126 | + """ |
| 127 | + Retrieves the lpmode (low power mode) status of this SFP |
| 128 | + Returns: |
| 129 | + A Boolean, True if lpmode is enabled, False if disabled |
| 130 | + """ |
| 131 | + try: |
| 132 | + reg_file = open(self.LP_PATH, "r") |
| 133 | + content = reg_file.readline().rstrip() |
| 134 | + except IOError as e: |
| 135 | + print("Error: unable to open file: %s" % str(e)) |
| 136 | + return False |
| 137 | + |
| 138 | + # content is a string containing the hex representation of the register |
| 139 | + reg_value = int(content, 16) |
| 140 | + |
| 141 | + # Determind if port_num start from 1 or 0 |
| 142 | + bit_index = self._index |
| 143 | + |
| 144 | + # Mask off the bit corresponding to our port |
| 145 | + mask = (1 << bit_index) |
| 146 | + |
| 147 | + # LPMode is active high |
| 148 | + if reg_value & mask == 0: |
| 149 | + return False |
| 150 | + |
| 151 | + return True |
| 152 | + |
| 153 | + def reset(self): |
| 154 | + """ |
| 155 | + Reset SFP and return all user module settings to their default srate. |
| 156 | + Returns: |
| 157 | + A boolean, True if successful, False if not |
| 158 | + """ |
| 159 | + # Check for invalid port_num |
| 160 | + |
| 161 | + try: |
| 162 | + reg_file = open(self.RESET_PATH, "r+") |
| 163 | + except IOError as e: |
| 164 | + print("Error: unable to open file: %s" % str(e)) |
| 165 | + return False |
| 166 | + |
| 167 | + content = reg_file.readline().rstrip() |
| 168 | + |
| 169 | + # File content is a string containing the hex representation of the |
| 170 | + # register |
| 171 | + reg_value = int(content, 16) |
| 172 | + |
| 173 | + # Determind if port_num start from 1 or 0 |
| 174 | + bit_index = self._index |
| 175 | + |
| 176 | + # Mask off the bit corresponding to our port |
| 177 | + mask = (1 << bit_index) |
| 178 | + |
| 179 | + # ResetL is active low |
| 180 | + reg_value = reg_value & ~mask |
| 181 | + |
| 182 | + # Convert our register value back to a hex string and write back |
| 183 | + reg_file.seek(0) |
| 184 | + reg_file.write(hex(reg_value).rstrip('L')) |
| 185 | + reg_file.close() |
| 186 | + |
| 187 | + # Sleep 1 second to allow it to settle |
| 188 | + time.sleep(1) |
| 189 | + |
| 190 | + # Flip the bit back high and write back to the register |
| 191 | + # to take port out of reset |
| 192 | + try: |
| 193 | + reg_file = open(self.RESET_PATH, "w") |
| 194 | + except IOError as e: |
| 195 | + print("Error: unable to open file: %s" % str(e)) |
| 196 | + return False |
| 197 | + |
| 198 | + reg_value = reg_value | mask |
| 199 | + reg_file.seek(0) |
| 200 | + reg_file.write(hex(reg_value).rstrip('L')) |
| 201 | + reg_file.close() |
| 202 | + |
| 203 | + return True |
| 204 | + |
| 205 | + def set_lpmode(self, lpmode): |
| 206 | + """ |
| 207 | + Sets the lpmode (low power mode) of SFP |
| 208 | + Args: |
| 209 | + lpmode: A Boolean, True to enable lpmode, False to disable it |
| 210 | + Note : lpmode can be overridden by set_power_override |
| 211 | + Returns: |
| 212 | + A boolean, True if lpmode is set successfully, False if not |
| 213 | + """ |
| 214 | + try: |
| 215 | + reg_file = open(self.LP_PATH, "r+") |
| 216 | + except IOError as e: |
| 217 | + print("Error: unable to open file: %s" % str(e)) |
| 218 | + return False |
| 219 | + |
| 220 | + content = reg_file.readline().rstrip() |
| 221 | + |
| 222 | + # content is a string containing the hex representation of the register |
| 223 | + reg_value = int(content, 16) |
| 224 | + |
| 225 | + # Determind if port_num start from 1 or 0 |
| 226 | + bit_index = self._index |
| 227 | + |
| 228 | + # Mask off the bit corresponding to our port |
| 229 | + mask = (1 << bit_index) |
| 230 | + # LPMode is active high; set or clear the bit accordingly |
| 231 | + reg_value = reg_value | mask if lpmode else reg_value & ~mask |
| 232 | + |
| 233 | + # Convert our register value back to a hex string and write back |
| 234 | + content = hex(reg_value).strip('L') |
| 235 | + |
| 236 | + reg_file.seek(0) |
| 237 | + reg_file.write(content) |
| 238 | + reg_file.close() |
| 239 | + |
| 240 | + return True |
| 241 | + |
| 242 | + def get_error_description(self): |
| 243 | + """ |
| 244 | + Retrives the error descriptions of the SFP module |
| 245 | + Returns: |
| 246 | + String that represents the current error descriptions of |
| 247 | + vendor specific errors |
| 248 | + In case there are multiple errors, they should be joined by '|', |
| 249 | + like: "Bad EEPROM|Unsupported cable" |
| 250 | + """ |
| 251 | + if self.sfp_type == "QSFP_DD": |
| 252 | + return super().get_error_description() |
| 253 | + |
| 254 | + if not self.get_presence(): |
| 255 | + return self.SFP_STATUS_UNPLUGGED |
| 256 | + return self.SFP_STATUS_OK |
| 257 | + |
| 258 | + def get_position_in_parent(self): |
| 259 | + return self._index |
| 260 | + |
| 261 | + def get_index(self): |
| 262 | + """ |
| 263 | + Retrieves current sfp index |
| 264 | + Returns: |
| 265 | + A int value, sfp index |
| 266 | + """ |
| 267 | + return self._index |
| 268 | + |
| 269 | + def is_replaceable(self): |
| 270 | + return True |
0 commit comments