diff --git a/device/dell/x86_64-dell_s6000_s1220-r0/pmon_daemon_control.json b/device/dell/x86_64-dell_s6000_s1220-r0/pmon_daemon_control.json index 44871c057e..94592fa8ce 100644 --- a/device/dell/x86_64-dell_s6000_s1220-r0/pmon_daemon_control.json +++ b/device/dell/x86_64-dell_s6000_s1220-r0/pmon_daemon_control.json @@ -1,4 +1,3 @@ { - "skip_ledd": true, - "skip_thermalctld": true + "skip_ledd": true } diff --git a/device/dell/x86_64-dell_s6100_c2538-r0/pmon_daemon_control.json b/device/dell/x86_64-dell_s6100_c2538-r0/pmon_daemon_control.json index 44871c057e..94592fa8ce 100644 --- a/device/dell/x86_64-dell_s6100_c2538-r0/pmon_daemon_control.json +++ b/device/dell/x86_64-dell_s6100_c2538-r0/pmon_daemon_control.json @@ -1,4 +1,3 @@ { - "skip_ledd": true, - "skip_thermalctld": true + "skip_ledd": true } diff --git a/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c b/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c index 3193e8b464..5712c0fbb5 100644 --- a/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c +++ b/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c @@ -174,6 +174,9 @@ /* Mailbox PowerOn Reason */ #define TRACK_POWERON_REASON 0x05FF +/* System Status LED */ +#define SYSTEM_STATUS_LED 0x04DF + /* CPU Set IO Modules */ #define CPU_IOM1_CTRL_FLAG 0x04D9 #define CPU_IOM2_CTRL_FLAG 0x04DA @@ -607,6 +610,44 @@ static ssize_t show_mb_poweron_reason(struct device *dev, return sprintf(buf, "0x%x\n", ret); } +/* System Status LED */ +static ssize_t set_sys_status_led(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + int err = 0; + unsigned int dev_data = 0; + struct smf_data *data = dev_get_drvdata(dev); + + if (data->kind == z9100smf) + return -1; + + err = kstrtouint(buf, 16, &dev_data); + if (err) + return err; + + err = smf_write_reg(data, SYSTEM_STATUS_LED, dev_data); + if(err < 0) + return err; + + return count; +} + +static ssize_t show_sys_status_led(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + unsigned int ret = 0; + struct smf_data *data = dev_get_drvdata(dev); + + if (data->kind == z9100smf) + return 0; + + ret = smf_read_reg(data, SYSTEM_STATUS_LED); + if(ret < 0) + return ret; + + return sprintf(buf, "0x%x\n", ret); +} + /* FANIN ATTR */ static ssize_t show_fan_label(struct device *dev, struct device_attribute *attr, char *buf) @@ -2081,12 +2122,17 @@ static SENSOR_DEVICE_ATTR(smf_poweron_reason, S_IRUGO, static SENSOR_DEVICE_ATTR(mb_poweron_reason, S_IRUGO|S_IWUSR, show_mb_poweron_reason, set_mb_poweron_reason, 0); +/* System Status LED */ +static SENSOR_DEVICE_ATTR(sys_status_led, S_IRUGO|S_IWUSR, + show_sys_status_led, set_sys_status_led, 0); + static struct attribute *smf_dell_attrs[] = { &sensor_dev_attr_smf_version.dev_attr.attr, &sensor_dev_attr_smf_firmware_ver.dev_attr.attr, &sensor_dev_attr_smf_reset_reason.dev_attr.attr, &sensor_dev_attr_smf_poweron_reason.dev_attr.attr, &sensor_dev_attr_mb_poweron_reason.dev_attr.attr, + &sensor_dev_attr_sys_status_led.dev_attr.attr, &sensor_dev_attr_fan_tray_presence.dev_attr.attr, &sensor_dev_attr_fan1_airflow.dev_attr.attr, &sensor_dev_attr_fan3_airflow.dev_attr.attr, diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/chassis.py index 899754709b..7f6e9ec573 100755 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/chassis.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/chassis.py @@ -24,7 +24,7 @@ except ImportError as e: MAX_S6000_FANTRAY = 3 MAX_S6000_PSU = 2 -MAX_S6000_THERMAL = 10 +MAX_S6000_THERMAL = 6 MAX_S6000_COMPONENT = 4 @@ -44,6 +44,8 @@ class Chassis(ChassisBase): def __init__(self): ChassisBase.__init__(self) + self.status_led_reg = "system_led" + self.supported_led_color = ['green', 'blinking green', 'amber', 'blinking amber'] # Initialize SFP list self.PORT_START = 0 self.PORT_END = 31 @@ -101,13 +103,30 @@ class Chassis(ChassisBase): try: with open(mb_reg_file, 'r') as fd: rv = fd.read() - except Exception as error: + except IOError: rv = 'ERR' rv = rv.rstrip('\r\n') rv = rv.lstrip(" ") return rv + def _set_cpld_register(self, reg_name, value): + # On successful write, returns the value will be written on + # reg_name and on failure returns 'ERR' + rv = 'ERR' + cpld_reg_file = self.CPLD_DIR+'/'+reg_name + + if (not os.path.isfile(cpld_reg_file)): + return rv + + try: + with open(cpld_reg_file, 'w') as fd: + rv = fd.write(str(value)) + except IOError: + rv = 'ERR' + + return rv + def _nvram_write(self, offset, val): resource = "/dev/nvram" fd = os.open(resource, os.O_RDWR) @@ -179,6 +198,23 @@ class Chassis(ChassisBase): """ return True + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether Chassis is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_base_mac(self): """ Retrieves the base MAC address for the chassis @@ -305,4 +341,41 @@ class Chassis(ChassisBase): return True, ret_dict return False, ret_dict + def set_status_led(self, color): + """ + Sets the state of the system LED + Args: + color: A string representing the color with which to set the + system LED + + Returns: + bool: True if system LED state is set successfully, False if not + """ + if color not in self.supported_led_color: + return False + + # Change color string format to the one used by driver + color = color.replace('amber', 'yellow') + color = color.replace('blinking ', 'blink_') + rv = self._set_cpld_register(self.status_led_reg, color) + if (rv != 'ERR'): + return True + else: + return False + + def get_status_led(self): + """ + Gets the state of the system LED + + Returns: + A string, one of the valid LED color strings which could be vendor + specified. + """ + status_led = self._get_cpld_register(self.status_led_reg) + if (status_led != 'ERR'): + status_led = status_led.replace('yellow', 'amber') + status_led = status_led.replace('blink_', 'blinking ') + return status_led + else: + return None diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/component.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/component.py index 1e21c233f5..d9459be47c 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/component.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/component.py @@ -29,8 +29,8 @@ class Component(ComponentBase): "booting")], ["System-CPLD", "Used for managing CPU board devices and power"], ["Master-CPLD", ("Used for managing Fan, PSU, system LEDs, QSFP " - "modules (1-16)")], - ["Slave-CPLD", "Used for managing QSFP modules (17-32)"] + "modules (17-32)")], + ["Slave-CPLD", "Used for managing QSFP modules (1-16)"] ] def __init__(self, component_index): @@ -90,6 +90,55 @@ class Component(ComponentBase): """ return self.name + def get_model(self): + """ + Retrieves the part number of the component + Returns: + string: Part number of component + """ + return 'NA' + + def get_serial(self): + """ + Retrieves the serial number of the component + Returns: + string: Serial number of component + """ + return 'NA' + + def get_presence(self): + """ + Retrieves the presence of the component + Returns: + bool: True if present, False if not + """ + return True + + def get_status(self): + """ + Retrieves the operational status of the component + Returns: + bool: True if component is operating properly, False if not + """ + return True + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether component is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_description(self): """ Retrieves the description of the component diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/eeprom.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/eeprom.py index ef736089c4..5afe011244 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/eeprom.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/eeprom.py @@ -36,8 +36,8 @@ psu_eeprom_format = [ ('Fab Rev', 's', 2) ] -# Fan eeprom fields in format required by EepromDecoder -fan_eeprom_format = [ +# FanTray eeprom fields in format required by EepromDecoder +fantray_eeprom_format = [ ('PPID', 's', 20), ('DPN Rev', 's', 3), ('Service Tag', 's', 7), ('Part Number', 's', 10), ('Part Num Revision', 's', 3), ('Mfg Test', 's', 2), ('Redundant copy', 's', 83), @@ -51,10 +51,10 @@ class Eeprom(TlvInfoDecoder): I2C_DIR = "/sys/class/i2c-adapter/" - def __init__(self, is_psu=False, psu_index=0, is_fan=False, fan_index=0): + def __init__(self, is_psu=False, psu_index=0, is_fantray=False, fantray_index=0): self.is_psu_eeprom = is_psu - self.is_fan_eeprom = is_fan - self.is_sys_eeprom = not (is_psu | is_fan) + self.is_fantray_eeprom = is_fantray + self.is_sys_eeprom = not (is_psu | is_fantray) if self.is_sys_eeprom: self.start_offset = 0 @@ -71,10 +71,10 @@ class Eeprom(TlvInfoDecoder): + "i2c-1/1-005{}/eeprom".format(2 - self.index) self.format = psu_eeprom_format else: - self.index = fan_index + self.index = fantray_index self.eeprom_path = self.I2C_DIR \ + "i2c-11/11-005{}/eeprom".format(4 - self.index) - self.format = fan_eeprom_format + self.format = fantray_eeprom_format EepromDecoder.__init__(self, self.eeprom_path, self.format, self.start_offset, '', True) self._load_device_eeprom() diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan.py index fbdbf650db..d5a4f379e9 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan.py @@ -13,7 +13,6 @@ try: import os import glob from sonic_platform_base.fan_base import FanBase - from sonic_platform.eeprom import Eeprom except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -25,33 +24,37 @@ MAX_S6000_FAN_SPEED = 19000 class Fan(FanBase): """DellEMC Platform-specific Fan class""" - CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0/" I2C_DIR = "/sys/class/i2c-adapter/" + FAN_DEV_MAPPING = { + 1: {1: ("i2c-11/11-002a", 1), 2: ("i2c-11/11-002a", 2)}, + 2: {1: ("i2c-11/11-0029", 3), 2: ("i2c-11/11-0029", 4)}, + 3: {1: ("i2c-11/11-0029", 1), 2: ("i2c-11/11-0029", 2)} + } - def __init__(self, fan_index, psu_fan=False, dependency=None): + def __init__(self, fantray_index=1, fan_index=1, + psu_index=1, psu_fan=False, dependency=None): self.is_psu_fan = psu_fan self.is_driver_initialized = True if not self.is_psu_fan: # Fan is 1-based in DellEMC platforms - self.index = fan_index + 1 - self.fan_presence_reg = "fan_prs" - self.fan_led_reg = "fan{}_led".format(fan_index) - self.get_fan_speed_reg = self.I2C_DIR + "i2c-11/11-0029/" +\ - "fan{}_input".format(self.index) - self.set_fan_speed_reg = self.I2C_DIR + "i2c-11/11-0029/" +\ - "fan{}_target".format(self.index) - self.eeprom = Eeprom(is_fan=True, fan_index=self.index) - self.max_fan_speed = MAX_S6000_FAN_SPEED - self.supported_led_color = ['off', 'green', 'amber'] - else: + self.fantray_index = fantray_index self.index = fan_index self.dependency = dependency + self.get_fan_speed_reg = self.I2C_DIR +\ + "{}/fan{}_input".format(*self.FAN_DEV_MAPPING[fantray_index][fan_index]) self.set_fan_speed_reg = self.I2C_DIR +\ - "i2c-1/1-005{}/fan1_target".format(10 - self.index) + "{}/fan{}_target".format(*self.FAN_DEV_MAPPING[fantray_index][fan_index]) + self.max_fan_speed = MAX_S6000_FAN_SPEED + else: + self.psu_index = psu_index + self.index = 1 + self.dependency = dependency + self.set_fan_speed_reg = self.I2C_DIR +\ + "i2c-1/1-005{}/fan1_target".format(10 - self.psu_index) hwmon_dir = self.I2C_DIR +\ - "i2c-1/1-005{}/hwmon/".format(10 - self.index) + "i2c-1/1-005{}/hwmon/".format(10 - self.psu_index) try: hwmon_node = os.listdir(hwmon_dir)[0] except OSError: @@ -61,43 +64,6 @@ class Fan(FanBase): self.get_fan_speed_reg = hwmon_dir + hwmon_node + '/fan1_input' self.max_fan_speed = MAX_S6000_PSU_FAN_SPEED - def _get_cpld_register(self, reg_name): - # On successful read, returns the value read from given - # reg_name and on failure returns 'ERR' - rv = 'ERR' - cpld_reg_file = self.CPLD_DIR + reg_name - - if (not os.path.isfile(cpld_reg_file)): - return rv - - try: - with open(cpld_reg_file, 'r') as fd: - rv = fd.read() - except: - rv = 'ERR' - - rv = rv.rstrip('\r\n') - rv = rv.lstrip(" ") - return rv - - def _set_cpld_register(self, reg_name, value): - # On successful write, returns the value will be written on - # reg_name and on failure returns 'ERR' - rv = 'ERR' - cpld_reg_file = self.CPLD_DIR + reg_name - - if (not os.path.isfile(cpld_reg_file)): - print("open error") - return rv - - try: - with open(cpld_reg_file, 'w') as fd: - rv = fd.write(str(value)) - except: - rv = 'ERR' - - return rv - def _get_i2c_register(self, reg_file): # On successful read, returns the value read from given # reg_name and on failure returns 'ERR' @@ -155,9 +121,9 @@ class Fan(FanBase): string: The name of the Fan """ if not self.is_psu_fan: - return "FanTray{}-Fan1".format(self.index) + return "FanTray{}-Fan{}".format(self.fantray_index, self.index) else: - return "PSU{} Fan".format(self.index) + return "PSU{} Fan".format(self.psu_index) def get_presence(self): """ @@ -166,42 +132,23 @@ class Fan(FanBase): Returns: bool: True if Fan is present, False if not """ - status = False - if self.is_psu_fan: - return self.dependency.get_presence() - - fan_presence = self._get_cpld_register(self.fan_presence_reg) - if (fan_presence != 'ERR'): - fan_presence = int(fan_presence,16) & self.index - if fan_presence: - status = True - - return status + return self.dependency.get_presence() def get_model(self): """ Retrieves the part number of the Fan - Returns: string: Part number of Fan """ - if not self.is_psu_fan: - return self.eeprom.get_part_number() - else: - return 'NA' + return 'NA' def get_serial(self): """ Retrieves the serial number of the Fan - Returns: string: Serial number of Fan """ - # Sample Serial number format "US-01234D-54321-25A-0123-A00" - if not self.is_psu_fan: - return self.eeprom.get_serial_number() - else: - return 'NA' + return 'NA' def get_status(self): """ @@ -218,6 +165,23 @@ class Fan(FanBase): return status + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_direction(self): """ Retrieves the fan airflow direction @@ -234,11 +198,10 @@ class Fan(FanBase): if self.is_psu_fan: direction = {1: self.FAN_DIRECTION_EXHAUST, 2: self.FAN_DIRECTION_INTAKE, 3: self.FAN_DIRECTION_EXHAUST, 4: self.FAN_DIRECTION_INTAKE} - fan_direction = self.dependency.eeprom.airflow_fan_type() else: direction = {1: self.FAN_DIRECTION_EXHAUST, 2: self.FAN_DIRECTION_INTAKE} - fan_direction = self.eeprom.airflow_fan_type() + fan_direction = self.dependency.eeprom.airflow_fan_type() return direction.get(fan_direction, self.FAN_DIRECTION_NOT_APPLICABLE) def get_speed(self): @@ -282,8 +245,8 @@ class Fan(FanBase): Returns: bool: True if set success, False if fail. """ - fan_set = (speed * self.max_fan_speed)/ 100 - rv = self._set_i2c_register(self.set_fan_speed_reg , fan_set) + fan_set = (speed * self.max_fan_speed) // 100 + rv = self._set_i2c_register(self.set_fan_speed_reg, fan_set) if (rv != 'ERR'): return True else: @@ -298,16 +261,9 @@ class Fan(FanBase): Returns: bool: True if set success, False if fail. """ - if self.is_psu_fan or (color not in self.supported_led_color): - return False - if(color == self.STATUS_LED_COLOR_AMBER): - color = 'yellow' - - rv = self._set_cpld_register(self.fan_led_reg ,color) - if (rv != 'ERR'): - return True - else: - return False + # No LED available for FanTray and PSU Fan + # Return True to avoid thermalctld alarm. + return True def get_status_led(self): """ @@ -316,18 +272,8 @@ class Fan(FanBase): Returns: A string, one of the predefined STATUS_LED_COLOR_* strings. """ - if self.is_psu_fan: - # No LED available for PSU Fan - return None - - fan_led = self._get_cpld_register(self.fan_led_reg) - if (fan_led != 'ERR'): - if (fan_led == 'yellow'): - return self.STATUS_LED_COLOR_AMBER - else: - return fan_led - else: - return self.STATUS_LED_COLOR_OFF + # No LED available for FanTray and PSU Fan + return None def get_target_speed(self): """ diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan_drawer.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan_drawer.py index c8ea283e5b..2e5e3446cd 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan_drawer.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/fan_drawer.py @@ -9,26 +9,176 @@ ######################################################################## try: + import os + from sonic_platform_base.fan_drawer_base import FanDrawerBase + from sonic_platform.eeprom import Eeprom from sonic_platform.fan import Fan except ImportError as e: raise ImportError(str(e) + "- required module not found") +MAX_S6000_FANS_PER_FANTRAY = 2 + class FanDrawer(FanDrawerBase): - """DellEMC Platform-specific Fan class""" + """DellEMC Platform-specific Fan Drawer class""" + + CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0/" def __init__(self, fantray_index): - FanDrawerBase.__init__(self) # FanTray is 1-based in DellEMC platforms - self.fantrayindex = fantray_index + 1 - self._fan_list.append(Fan(fantray_index)) + self.index = fantray_index + 1 + self.eeprom = Eeprom(is_fantray=True, fantray_index=self.index) + self.fantray_presence_reg = "fan_prs" + self.fantray_led_reg = "fan{}_led".format(self.index - 1) + self.supported_led_color = ['off', 'green', 'amber'] + + for i in range(1, MAX_S6000_FANS_PER_FANTRAY+1): + self._fan_list.append(Fan(fantray_index=self.index, fan_index=i, dependency=self)) + + def _get_cpld_register(self, reg_name): + # On successful read, returns the value read from given + # reg_name and on failure returns 'ERR' + rv = 'ERR' + cpld_reg_file = self.CPLD_DIR + reg_name + + if (not os.path.isfile(cpld_reg_file)): + return rv + + try: + with open(cpld_reg_file, 'r') as fd: + rv = fd.read() + except IOError: + rv = 'ERR' + + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + + def _set_cpld_register(self, reg_name, value): + # On successful write, returns the value will be written on + # reg_name and on failure returns 'ERR' + rv = 'ERR' + cpld_reg_file = self.CPLD_DIR + reg_name + + if (not os.path.isfile(cpld_reg_file)): + return rv + + try: + with open(cpld_reg_file, 'w') as fd: + rv = fd.write(str(value)) + except IOError: + rv = 'ERR' + + return rv def get_name(self): """ - Retrieves the fan drawer name + Retrieves the Fandrawer name Returns: string: The name of the device """ - return "FanTray{}".format(self.fantrayindex) + return "FanTray{}".format(self.index) + + def get_presence(self): + """ + Retrieves the presence of the Fandrawer + + Returns: + bool: True if Fandrawer is present, False if not + """ + presence = False + + fantray_presence = self._get_cpld_register(self.fantray_presence_reg) + if (fantray_presence != 'ERR'): + fantray_presence = int(fantray_presence, 16) & (1 << (self.index - 1)) + if fantray_presence: + presence = True + + return presence + + def get_model(self): + """ + Retrieves the part number of the Fandrawer + + Returns: + string: Part number of Fandrawer + """ + return self.eeprom.get_part_number() + + def get_serial(self): + """ + Retrieves the serial number of the Fandrawer + + Returns: + string: Serial number of Fandrawer + """ + # Sample Serial number format "US-01234D-54321-25A-0123-A00" + return self.eeprom.get_serial_number() + + def get_status(self): + """ + Retrieves the operational status of the Fandrawer + + Returns: + bool: True if Fandrawer is operating properly, False if not + """ + status = True + for fan in self.get_all_fans(): + status &= fan.get_status() + + return status + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this fan drawer is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def set_status_led(self, color): + """ + Set led to expected color + Args: + color: A string representing the color with which to set the + fandrawer status LED + Returns: + bool: True if set success, False if fail. + """ + if color not in self.supported_led_color: + return False + if color == self.STATUS_LED_COLOR_AMBER: + color = 'yellow' + + rv = self._set_cpld_register(self.fantray_led_reg, color) + if (rv != 'ERR'): + return True + else: + return False + + def get_status_led(self): + """ + Gets the state of the fandrawer status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + fantray_led = self._get_cpld_register(self.fantray_led_reg) + if (fantray_led != 'ERR'): + if (fantray_led == 'yellow'): + return self.STATUS_LED_COLOR_AMBER + else: + return fantray_led + else: + return self.STATUS_LED_COLOR_OFF diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/psu.py index af52ccedbc..e217c0a08c 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/psu.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/psu.py @@ -15,9 +15,12 @@ try: from sonic_platform_base.psu_base import PsuBase from sonic_platform.eeprom import Eeprom from sonic_platform.fan import Fan + from sonic_platform.thermal import Thermal except ImportError as e: raise ImportError(str(e) + "- required module not found") +MAX_S6000_THERMALS_PER_PSU = 2 + class Psu(PsuBase): """DellEMC Platform-specific PSU class""" @@ -53,7 +56,10 @@ class Psu(PsuBase): self.eeprom = Eeprom(is_psu=True, psu_index=self.index) - self._fan_list.append(Fan(self.index, psu_fan=True, dependency=self)) + self._fan_list.append(Fan(psu_index=self.index, psu_fan=True, dependency=self)) + for i in range(1, MAX_S6000_THERMALS_PER_PSU+1): + self._thermal_list.append(Thermal(psu_index=self.index, thermal_index=i, + psu_thermal=True, dependency=self)) def _get_cpld_register(self, reg_name): # On successful read, returns the value read from given @@ -171,6 +177,23 @@ class Psu(PsuBase): return status + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether PSU is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + def get_voltage(self): """ Retrieves current PSU voltage output @@ -266,3 +289,50 @@ class Psu(PsuBase): # In S6000, the firmware running in the PSU controls the LED # and the PSU LED state cannot be changed from CPU. return False + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + + Returns: + A float number of current temperature in Celsius up to + nearest thousandth of one degree Celsius, e.g. 30.125 + """ + if self.get_presence(): + return self.get_thermal(0).get_temperature() + else: + return 0.0 + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + + Returns: + A float number, the high threshold temperature of PSU in + Celsius up to nearest thousandth of one degree Celsius, + e.g. 30.125 + """ + if self.get_presence(): + return self.get_thermal(0).get_high_threshold() + else: + return 0.0 + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + return 12.6 + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + return 11.4 diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/sfp.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/sfp.py index 175e601dbe..b66cb31ec9 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/sfp.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/sfp.py @@ -10,6 +10,7 @@ try: import re + import struct import time from sonic_platform_base.sfp_base import SfpBase from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId @@ -27,6 +28,9 @@ INFO_OFFSET = 128 DOM_OFFSET = 0 DOM_OFFSET1 = 384 +QSFP_CONTROL_OFFSET = 86 +QSFP_POWEROVERRIDE_OFFSET = 93 + cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)', 'Length OM1(m)', 'Length Cable Assembly(m)') @@ -108,7 +112,7 @@ class Sfp(SfpBase): sfp_control, sfp_ctrl_idx): SfpBase.__init__(self) self.sfp_type = sfp_type - self.index = index + self.index = index + 1 self.eeprom_path = eeprom_path self.sfp_control = sfp_control self.sfp_ctrl_idx = sfp_ctrl_idx @@ -132,9 +136,10 @@ class Sfp(SfpBase): eeprom.close() return None + raw = bytearray(raw) try: for n in range(0, num_bytes): - eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) except BaseException: eeprom.close() return None @@ -588,40 +593,14 @@ class Sfp(SfpBase): """ Retrieves the TX disabled channels in this SFP """ - tx_disable = None - tx_disable_list = [] + tx_disable_channel = 0 - tx_disable_data = self._get_eeprom_data('tx_disable') - if (tx_disable_data is not None): - tx_disable = tx_disable_data['data']['Tx1Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) - tx_disable = tx_disable_data['data']['Tx2Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) - tx_disable = tx_disable_data['data']['Tx3Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) - tx_disable = tx_disable_data['data']['Tx4Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) + tx_disable = self.get_tx_disable() + for channel, disable in enumerate(tx_disable): + if disable: + tx_disable_channel |= 1 << channel - bit4 = int(tx_disable_list[3]) * 8 - bit3 = int(tx_disable_list[2]) * 4 - bit2 = int(tx_disable_list[1]) * 2 - bit1 = int(tx_disable_list[0]) * 1 - - tx_disable_channel = hex(bit4 + bit3 + bit2 + bit1) - - return tx_disable_channel + return tx_disable_channel def get_lpmode(self): """ @@ -722,7 +701,6 @@ class Sfp(SfpBase): return rx_power_list - def get_tx_power(self): """ Retrieves the TX power of this SFP @@ -822,19 +800,68 @@ class Sfp(SfpBase): """ Disable SFP TX for all channels """ - return False + eeprom = None + tx_disable_value = 0xf if tx_disable else 0x0 + + try: + eeprom = open(self.eeprom_path, "r+b") + eeprom.seek(QSFP_CONTROL_OFFSET) + eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + return True def tx_disable_channel(self, channel, disable): """ Sets the tx_disable for specified SFP channels """ - return False + eeprom = None + current_state = self.get_tx_disable_channel() + + if disable: + tx_disable_value = current_state | channel + else: + tx_disable_value = current_state & (~channel) + + try: + eeprom = open(self.eeprom_path, "r+b") + eeprom.seek(QSFP_CONTROL_OFFSET) + eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + return True def set_power_override(self, power_override, power_set): """ Sets SFP power level using power_override and power_set """ - return False + eeprom = None + power_override_bit = 0x1 if power_override else 0 + power_set_bit = 0x2 if power_set else 0 + value = power_override_bit | power_set_bit + + try: + eeprom = open(self.eeprom_path, "r+b") + eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) + eeprom.write(struct.pack('B', value)) + except IOError: + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + return True def get_status(self): """ @@ -848,3 +875,20 @@ class Sfp(SfpBase): status = True return status + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/thermal.py b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/thermal.py index 2f130ebabb..ad089a8946 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/thermal.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6000/sonic_platform/thermal.py @@ -13,7 +13,6 @@ try: import os import glob from sonic_platform_base.thermal_base import ThermalBase - from sonic_platform.psu import Psu except ImportError as e: raise ImportError(str(e) + "- required module not found") @@ -34,11 +33,20 @@ class Thermal(ThermalBase): 'PSU1-Sensor 1', 'PSU1-Sensor 2', 'PSU2-Sensor 1', 'PSU2-Sensor 2', 'CPU Core 0', 'CPU Core 1') - def __init__(self, thermal_index): - self.index = thermal_index + 1 - self.is_psu_thermal = False + def __init__(self, thermal_index, + psu_index=1, psu_thermal=False, dependency=None): + self.is_psu_thermal = psu_thermal + self.dependency = dependency self.is_driver_initialized = True - self.dependency = None + + if self.is_psu_thermal: + self.index = (2 * psu_index) + thermal_index + 2 + else: + # CPU thermal + if thermal_index > 3: + self.index = thermal_index + 5 + else: + self.index = thermal_index + 1 if self.index < 9: i2c_path = self.I2C_DIR + self.I2C_DEV_MAPPING[self.index - 1][0] @@ -54,10 +62,6 @@ class Thermal(ThermalBase): if self.index == 4: hwmon_temp_suffix = "crit" - - if self.index > 4: - self.is_psu_thermal = True - self.dependency = Psu(self.index / 7) else: dev_path = "/sys/devices/platform/coretemp.0/hwmon/" hwmon_temp_index = self.index - 7 @@ -168,6 +172,23 @@ class Thermal(ThermalBase): else: return True + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether Thermal is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_temperature(self): """ Retrieves current temperature reading from thermal diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py index 47ef87b951..f8e009cdbe 100755 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py @@ -54,9 +54,21 @@ class Chassis(ChassisBase): power_reason_dict[33] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC power_reason_dict[44] = ChassisBase.REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED + status_led_reg_to_color = { + 0x00: 'green', 0x01: 'blinking green', 0x02: 'amber', + 0x04: 'amber', 0x08: 'blinking amber', 0x10: 'blinking amber' + } + + color_to_status_led_reg = { + 'green': 0x00, 'blinking green': 0x01, + 'amber': 0x02, 'blinking amber': 0x08 + } + def __init__(self): ChassisBase.__init__(self) + self.status_led_reg = "sys_status_led" + self.supported_led_color = ['green', 'blinking green', 'amber', 'blinking amber'] # Initialize EEPROM self._eeprom = Eeprom() for i in range(MAX_S6100_MODULE): @@ -113,6 +125,23 @@ class Chassis(ChassisBase): rv = rv.lstrip(" ") return rv + def _set_pmc_register(self, reg_name, value): + # On successful write, returns the length of value written on + # reg_name and on failure returns 'ERR' + rv = 'ERR' + mb_reg_file = self.MAILBOX_DIR + '/' + reg_name + + if (not os.path.isfile(mb_reg_file)): + return rv + + try: + with open(mb_reg_file, 'w') as fd: + rv = fd.write(str(value)) + except IOError: + rv = 'ERR' + + return rv + def _get_register(self, reg_file): # On successful read, returns the value read from given # reg_name and on failure returns 'ERR' @@ -172,6 +201,23 @@ class Chassis(ChassisBase): """ return True + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether Chassis is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_base_mac(self): """ Retrieves the base MAC address for the chassis @@ -325,3 +371,38 @@ class Chassis(ChassisBase): break return True, ret_dict + + def set_status_led(self, color): + """ + Sets the state of the system LED + + Args: + color: A string representing the color with which to set the + system LED + + Returns: + bool: True if system LED state is set successfully, False if not + """ + if color not in self.supported_led_color: + return False + + value = self.color_to_status_led_reg[color] + rv = self._set_pmc_register(self.status_led_reg, value) + if (rv != 'ERR'): + return True + else: + return False + + def get_status_led(self): + """ + Gets the state of the system LED + + Returns: + A string, one of the valid LED color strings which could be + vendor specified. + """ + reg_val = self._get_pmc_register(self.status_led_reg) + if (reg_val != 'ERR'): + return self.status_led_reg_to_color.get(int(reg_val, 16), None) + else: + return None diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/component.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/component.py index 6b1420a986..bea180d440 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/component.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/component.py @@ -39,9 +39,10 @@ class Component(ComponentBase): ] def __init__(self, component_index=0, - is_module=False, iom_index=0, i2c_line=0): + is_module=False, iom_index=0, i2c_line=0, dependency=None): self.is_module_component = is_module + self.dependency = dependency if self.is_module_component: self.index = iom_index @@ -132,6 +133,61 @@ class Component(ComponentBase): """ return self.name + def get_model(self): + """ + Retrieves the part number of the component + Returns: + string: Part number of component + """ + return 'NA' + + def get_serial(self): + """ + Retrieves the serial number of the component + Returns: + string: Serial number of component + """ + return 'NA' + + def get_presence(self): + """ + Retrieves the presence of the component + Returns: + bool: True if present, False if not + """ + if self.is_module_component: + return self.dependency.get_presence() + else: + return True + + def get_status(self): + """ + Retrieves the operational status of the component + Returns: + bool: True if component is operating properly, False if not + """ + if self.is_module_component: + return self.dependency.get_status() + else: + return True + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether component is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_description(self): """ Retrieves the description of the component diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py index 8404a8d2bf..5b3c8977ac 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py @@ -26,30 +26,23 @@ class Fan(FanBase): HWMON_NODE = os.listdir(HWMON_DIR)[0] MAILBOX_DIR = HWMON_DIR + HWMON_NODE - def __init__(self, fantray_index=1, fan_index=1, psu_fan=False): + def __init__(self, fantray_index=1, psu_index=1, psu_fan=False, dependency=None): self.is_psu_fan = psu_fan if not self.is_psu_fan: - # API index is starting from 0, DellEMC platform index is starting - # from 1 - self.fantrayindex = fantray_index + 1 - self.fanindex = fan_index + 1 - self.fan_presence_reg = "fan{}_fault".format( - 2 * self.fantrayindex - 1) + self.fantrayindex = fantray_index + self.dependency = dependency self.fan_status_reg = "fan{}_alarm".format( 2 * self.fantrayindex - 1) self.get_fan_speed_reg = "fan{}_input".format( 2 * self.fantrayindex - 1) self.get_fan_dir_reg = "fan{}_airflow".format( 2 * self.fantrayindex - 1) - self.fan_serialno_reg = "fan{}_serialno".format( - 2 * self.fantrayindex - 1) self.max_fan_speed = MAX_S6100_FAN_SPEED else: - # PSU Fan index starts from 11 - self.fanindex = fan_index + 10 - self.fan_presence_reg = "fan{}_fault".format(self.fanindex) - self.get_fan_speed_reg = "fan{}_input".format(self.fanindex) - self.get_fan_dir_reg = "fan{}_airflow".format(self.fanindex) + self.psuindex = psu_index + self.fan_presence_reg = "fan{}_fault".format(self.psuindex + 10) + self.get_fan_speed_reg = "fan{}_input".format(self.psuindex + 10) + self.get_fan_dir_reg = "fan{}_airflow".format(self.psuindex + 10) self.max_fan_speed = MAX_S6100_PSU_FAN_SPEED def _get_pmc_register(self, reg_name): @@ -77,10 +70,9 @@ class Fan(FanBase): string: The name of the device """ if not self.is_psu_fan: - return "FanTray{}-Fan{}".format( - self.fantrayindex, self.fanindex - 1) + return "FanTray{}-Fan1".format(self.fantrayindex) else: - return "PSU{} Fan".format(self.fanindex - 10) + return "PSU{} Fan".format(self.psuindex) def get_model(self): """ @@ -88,21 +80,7 @@ class Fan(FanBase): Returns: string: Part number of FAN """ - # For Serial number "US-01234D-54321-25A-0123-A00", the part - # number is "01234D" - if self.is_psu_fan: - return 'NA' - - fan_serialno = self._get_pmc_register(self.fan_serialno_reg) - if (fan_serialno != 'ERR') and self.get_presence(): - if (len(fan_serialno.split('-')) > 1): - fan_partno = fan_serialno.split('-')[1] - else: - fan_partno = 'NA' - else: - fan_partno = 'NA' - - return fan_partno + return 'NA' def get_serial(self): """ @@ -110,15 +88,7 @@ class Fan(FanBase): Returns: string: Serial number of FAN """ - # Sample Serial number format "US-01234D-54321-25A-0123-A00" - if self.is_psu_fan: - return 'NA' - - fan_serialno = self._get_pmc_register(self.fan_serialno_reg) - if (fan_serialno == 'ERR') or not self.get_presence(): - fan_serialno = 'NA' - - return fan_serialno + return 'NA' def get_presence(self): """ @@ -126,14 +96,17 @@ class Fan(FanBase): Returns: bool: True if fan is present, False if not """ - status = False - fantray_presence = self._get_pmc_register(self.fan_presence_reg) - if (fantray_presence != 'ERR'): - fantray_presence = int(fantray_presence, 10) - if (~fantray_presence & 0b1): - status = True + if not self.is_psu_fan: + return self.dependency.get_presence() - return status + presence = False + fan_presence = self._get_pmc_register(self.fan_presence_reg) + if (fan_presence != 'ERR'): + fan_presence = int(fan_presence, 10) + if (~fan_presence & 0b1): + presence = True + + return presence def get_status(self): """ @@ -157,6 +130,23 @@ class Fan(FanBase): return status + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return 1 + + def is_replaceable(self): + """ + Indicate whether Fan is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_direction(self): """ Retrieves the fan airflow direction @@ -216,7 +206,6 @@ class Fan(FanBase): Returns: bool: True if set success, False if fail. """ - # Fan speeds are controlled by Smart-fussion FPGA. return False @@ -229,7 +218,7 @@ class Fan(FanBase): Returns: bool: True if set success, False if fail. """ - # Leds are controlled by Smart-fussion FPGA. + # No LED available for FanTray and PSU Fan # Return True to avoid thermalctld alarm. return True @@ -240,17 +229,8 @@ class Fan(FanBase): Returns: A string, one of the predefined STATUS_LED_COLOR_* strings. """ - if self.is_psu_fan: - # No LED available for PSU Fan - return None - else: - if self.get_presence(): - if self.get_status(): - return self.STATUS_LED_COLOR_GREEN - else: - return self.STATUS_LED_COLOR_AMBER - else: - return self.STATUS_LED_COLOR_OFF + # No LED available for FanTray and PSU Fan + return None def get_target_speed(self): """ @@ -262,5 +242,3 @@ class Fan(FanBase): # Fan speeds are controlled by Smart-fussion FPGA. # Return current speed to avoid false thermalctld alarm. return self.get_speed() - - diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan_drawer.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan_drawer.py index ada5e93393..41e870a639 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan_drawer.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan_drawer.py @@ -9,6 +9,8 @@ ######################################################################## try: + import os + from sonic_platform_base.fan_drawer_base import FanDrawerBase from sonic_platform.fan import Fan except ImportError as e: @@ -16,14 +18,38 @@ except ImportError as e: class FanDrawer(FanDrawerBase): - """DellEMC Platform-specific Fan class""" + """DellEMC Platform-specific Fan Drawer class""" + + HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/" + HWMON_NODE = os.listdir(HWMON_DIR)[0] + MAILBOX_DIR = HWMON_DIR + HWMON_NODE def __init__(self, fantray_index): - FanDrawerBase.__init__(self) # FanTray is 1-based in DellEMC platforms - self.fantrayindex = fantray_index + 1 - self._fan_list.append(Fan(fantray_index)) + self.index = fantray_index + 1 + self.presence_reg = "fan{}_fault".format(2 * self.index - 1) + self.serialno_reg = "fan{}_serialno".format(2 * self.index - 1) + + self._fan_list.append(Fan(self.index, dependency=self)) + + def _get_pmc_register(self, reg_name): + # On successful read, returns the value read from given + # reg_name and on failure returns 'ERR' + rv = 'ERR' + mb_reg_file = self.MAILBOX_DIR+'/'+reg_name + + if (not os.path.isfile(mb_reg_file)): + return rv + try: + with open(mb_reg_file, 'r') as fd: + rv = fd.read() + except Exception as error: + rv = 'ERR' + + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv def get_name(self): """ @@ -31,4 +57,105 @@ class FanDrawer(FanDrawerBase): Returns: string: The name of the device """ - return "FanTray{}".format(self.fantrayindex) + return "FanTray{}".format(self.index) + + def get_model(self): + """ + Retrieves the part number of Fandrawer + Returns: + string: Part number of Fandrawer + """ + # For Serial number "US-01234D-54321-25A-0123-A00", the part + # number is "01234D" + fantray_serialno = self._get_pmc_register(self.serialno_reg) + if (fantray_serialno != 'ERR') and self.get_presence(): + if (len(fantray_serialno.split('-')) > 1): + fantray_partno = fantray_serialno.split('-')[1] + else: + fantray_partno = 'NA' + else: + fantray_partno = 'NA' + + return fantray_partno + + def get_serial(self): + """ + Retrieves the serial number of Fandrawer + Returns: + string: Serial number of Fandrawer + """ + # Sample Serial number format "US-01234D-54321-25A-0123-A00" + fantray_serialno = self._get_pmc_register(self.serialno_reg) + if (fantray_serialno == 'ERR') or not self.get_presence(): + fantray_serialno = 'NA' + + return fantray_serialno + + def get_presence(self): + """ + Retrieves the presence of the Fandrawer + Returns: + bool: True if fan is present, False if not + """ + presence = False + fantray_presence = self._get_pmc_register(self.presence_reg) + if (fantray_presence != 'ERR'): + fantray_presence = int(fantray_presence, 10) + if (~fantray_presence & 0b1): + presence = True + + return presence + + def get_status(self): + """ + Retrieves the operational status of the Fandrawer + + Returns: + bool: True if Fandrawer is operating properly, False if not + """ + return self.get_fan(0).get_status() + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this Fandrawer is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def set_status_led(self, color): + """ + Set led to expected color + Args: + color: A string representing the color with which to set the + fan module status LED + Returns: + bool: True if set success, False if fail. + """ + # Leds are controlled by Smart-fussion FPGA. + # Return True to avoid thermalctld alarm. + return True + + def get_status_led(self): + """ + Gets the state of the Fan status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + if self.get_presence(): + if self.get_fan(0).get_status(): + return self.STATUS_LED_COLOR_GREEN + else: + return self.STATUS_LED_COLOR_AMBER + else: + return self.STATUS_LED_COLOR_OFF diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/module.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/module.py index 6b93bcfd1f..923bb5c055 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/module.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/module.py @@ -63,7 +63,7 @@ class Module(ModuleBase): self.iom_presence_reg = "iom_presence" component = Component(is_module=True, iom_index=self.index, - i2c_line=self.port_i2c_line) + i2c_line=self.port_i2c_line, dependency=self) self._component_list.append(component) eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/i2c-{1}/{1}-0050/eeprom" @@ -157,6 +157,23 @@ class Module(ModuleBase): return status + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether Module is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + def get_base_mac(self): """ Retrieves the base MAC address for the module diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py index 48b266daa2..4e528b679e 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py @@ -34,13 +34,15 @@ class Psu(PsuBase): self.psu_voltage_reg = "in30_input" self.psu_current_reg = "curr602_input" self.psu_power_reg = "power2_input" + self.psu_temperature_reg = "temp14_input" elif self.index == 2: self.psu_voltage_reg = "in32_input" self.psu_current_reg = "curr702_input" self.psu_power_reg = "power4_input" + self.psu_temperature_reg = "temp15_input" # Passing True to specify it is a PSU fan - psu_fan = Fan(fan_index=self.index, psu_fan=True) + psu_fan = Fan(psu_index=self.index, psu_fan=True) self._fan_list.append(psu_fan) def _get_pmc_register(self, reg_name): @@ -232,3 +234,87 @@ class Psu(PsuBase): # In S6100, SmartFusion FPGA controls the PSU LED and the PSU # LED state cannot be changed from CPU. return False + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this PSU is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_temperature(self): + """ + Retrieves current temperature reading from PSU + + Returns: + A float number of current temperature in Celsius up to + nearest thousandth of one degree Celsius, e.g. 30.125 + """ + temperature = 0.0 + if self.get_presence(): + psu_temperature = self._get_pmc_register(self.psu_temperature_reg) + if (psu_temperature != 'ERR'): + temperature = float(psu_temperature) / 1000 + + return temperature + + def get_temperature_high_threshold(self): + """ + Retrieves the high threshold temperature of PSU + + Returns: + A float number, the high threshold temperature of PSU in + Celsius up to nearest thousandth of one degree Celsius, + e.g. 30.125 + """ + return 90.0 + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + voltage_high_threshold = 0.0 + if self.get_presence(): + psu_type = self._get_pmc_register(self.psu_presence_reg) + if (psu_type != 'ERR'): + psu_type = int(psu_type, 16) + if (psu_type & 0b10): + voltage_high_threshold = 12.6 + else: + voltage_high_threshold = 12.8 + + return voltage_high_threshold + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + voltage_low_threshold = 0.0 + if self.get_presence(): + psu_type = self._get_pmc_register(self.psu_presence_reg) + if (psu_type != 'ERR'): + psu_type = int(psu_type, 16) + if (psu_type & 0b10): + voltage_low_threshold = 11.4 + else: + voltage_low_threshold = 11.6 + + return voltage_low_threshold diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/sfp.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/sfp.py index 6947b6cf2b..0b242ab74d 100755 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/sfp.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/sfp.py @@ -10,6 +10,7 @@ try: import re + import struct import time from sonic_platform_base.sfp_base import SfpBase from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId @@ -27,6 +28,9 @@ INFO_OFFSET = 128 DOM_OFFSET = 0 DOM_OFFSET1 = 384 +QSFP_CONTROL_OFFSET = 86 +QSFP_POWEROVERRIDE_OFFSET = 93 + cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)', 'Length OM1(m)', 'Length Cable Assembly(m)') @@ -107,7 +111,7 @@ class Sfp(SfpBase): sfp_control, sfp_ctrl_idx): SfpBase.__init__(self) self.sfp_type = sfp_type - self.index = index + self.index = index + 1 self.eeprom_path = eeprom_path self.sfp_control = sfp_control self.sfp_ctrl_idx = sfp_ctrl_idx @@ -131,9 +135,10 @@ class Sfp(SfpBase): eeprom.close() return None + raw = bytearray(raw) try: for n in range(0, num_bytes): - eeprom_raw[n] = hex(ord(raw[n]))[2:].zfill(2) + eeprom_raw[n] = hex(raw[n])[2:].zfill(2) except BaseException: eeprom.close() return None @@ -594,40 +599,14 @@ class Sfp(SfpBase): """ Retrieves the TX disabled channels in this SFP """ - tx_disable = None - tx_disable_list = [] + tx_disable_channel = 0 - tx_disable_data = self._get_eeprom_data('tx_disable') - if (tx_disable_data is not None): - tx_disable = tx_disable_data['data']['Tx1Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) - tx_disable = tx_disable_data['data']['Tx2Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) - tx_disable = tx_disable_data['data']['Tx3Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) - tx_disable = tx_disable_data['data']['Tx4Disable']['value'] - if (tx_disable == 'On'): - tx_disable_list.append(1) - else: - tx_disable_list.append(0) + tx_disable = self.get_tx_disable() + for channel, disable in enumerate(tx_disable): + if disable: + tx_disable_channel |= 1 << channel - bit4 = int(tx_disable_list[3]) * 8 - bit3 = int(tx_disable_list[2]) * 4 - bit2 = int(tx_disable_list[1]) * 2 - bit1 = int(tx_disable_list[0]) * 1 - - tx_disable_channel = hex(bit4 + bit3 + bit2 + bit1) - - return tx_disable_channel + return tx_disable_channel def get_lpmode(self): """ @@ -836,19 +815,68 @@ class Sfp(SfpBase): """ Disable SFP TX for all channels """ - return False + eeprom = None + tx_disable_value = 0xf if tx_disable else 0x0 + + try: + eeprom = open(self.eeprom_path, "r+b") + eeprom.seek(QSFP_CONTROL_OFFSET) + eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + return True def tx_disable_channel(self, channel, disable): """ Sets the tx_disable for specified SFP channels """ - return False + eeprom = None + current_state = self.get_tx_disable_channel() + + if disable: + tx_disable_value = current_state | channel + else: + tx_disable_value = current_state & (~channel) + + try: + eeprom = open(self.eeprom_path, "r+b") + eeprom.seek(QSFP_CONTROL_OFFSET) + eeprom.write(struct.pack('B', tx_disable_value)) + except IOError: + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + return True def set_power_override(self, power_override, power_set): """ Sets SFP power level using power_override and power_set """ - return False + eeprom = None + power_override_bit = 0x1 if power_override else 0 + power_set_bit = 0x2 if power_set else 0 + value = power_override_bit | power_set_bit + + try: + eeprom = open(self.eeprom_path, "r+b") + eeprom.seek(QSFP_POWEROVERRIDE_OFFSET) + eeprom.write(struct.pack('B', value)) + except IOError: + return False + finally: + if eeprom is not None: + eeprom.close() + time.sleep(0.01) + + return True def get_status(self): """ @@ -862,3 +890,20 @@ class Sfp(SfpBase): status = True return status + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/thermal.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/thermal.py index f7037b000c..cac17f4b83 100644 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/thermal.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/thermal.py @@ -131,6 +131,23 @@ class Thermal(ThermalBase): return status + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. + Returns: + integer: The 1-based relative physical position in parent + device or -1 if cannot determine the position + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this Thermal is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + def get_temperature(self): """ Retrieves current temperature reading from thermal