DellEMC: S6100, S6000 - Enable thermalctld, Platform API implementation and fixes (#6438)

**- Why I did it**

To incorporate the below changes in DellEMC S6100, S6000 platforms.

- S6100, S6000:
    - Enable 'thermalctld'
    - Implement DeviceBase methods (presence, status, model, serial) for Fantray and Component
    - Implement ‘get_position_in_parent’, ‘is_replaceable’ methods for all device types
    - Implement ‘get_status’ method for Fantray
    - Implement ‘get_temperature’, ‘get_temperature_high_threshold’, ‘get_voltage_high_threshold’, ‘get_voltage_low_threshold’ methods for PSU
    - Implement ‘get_status_led’, ‘set_status_led’ methods for Chassis
    - SFP:
        - Make EEPROM read both Python2 and Python3 compatible
        - Fix ‘get_tx_disable_channel’ method’s return type
        - Implement ‘tx_disable’, ‘tx_disable_channel’ and ‘set_power_override’ methods
- S6000:
    - Move PSU thermal sensors from Chassis to respective PSU
    - Make available the data of both Fans present in each Fantray


**- How I did it**

- Remove 'skip_thermalctld:true' in pmon_daemon_control.json
- Implement the platform API methods in the respective device files
- Use `bytearray` for data read from transceiver EEPROM 
- Change return type of 'get_tx_disable_channel' to match specification in sonic_platform_common/sfp_base.py
This commit is contained in:
Arun Saravanan Balachandran 2021-02-05 20:30:08 +00:00 committed by Danny Allen
parent 276fade8c2
commit 0bae3b44ec
19 changed files with 1084 additions and 280 deletions

View File

@ -1,4 +1,3 @@
{ {
"skip_ledd": true, "skip_ledd": true
"skip_thermalctld": true
} }

View File

@ -1,4 +1,3 @@
{ {
"skip_ledd": true, "skip_ledd": true
"skip_thermalctld": true
} }

View File

@ -174,6 +174,9 @@
/* Mailbox PowerOn Reason */ /* Mailbox PowerOn Reason */
#define TRACK_POWERON_REASON 0x05FF #define TRACK_POWERON_REASON 0x05FF
/* System Status LED */
#define SYSTEM_STATUS_LED 0x04DF
/* CPU Set IO Modules */ /* CPU Set IO Modules */
#define CPU_IOM1_CTRL_FLAG 0x04D9 #define CPU_IOM1_CTRL_FLAG 0x04D9
#define CPU_IOM2_CTRL_FLAG 0x04DA #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); 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 */ /* FANIN ATTR */
static ssize_t static ssize_t
show_fan_label(struct device *dev, struct device_attribute *attr, char *buf) 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, static SENSOR_DEVICE_ATTR(mb_poweron_reason, S_IRUGO|S_IWUSR,
show_mb_poweron_reason, set_mb_poweron_reason, 0); 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[] = { static struct attribute *smf_dell_attrs[] = {
&sensor_dev_attr_smf_version.dev_attr.attr, &sensor_dev_attr_smf_version.dev_attr.attr,
&sensor_dev_attr_smf_firmware_ver.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_reset_reason.dev_attr.attr,
&sensor_dev_attr_smf_poweron_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_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_fan_tray_presence.dev_attr.attr,
&sensor_dev_attr_fan1_airflow.dev_attr.attr, &sensor_dev_attr_fan1_airflow.dev_attr.attr,
&sensor_dev_attr_fan3_airflow.dev_attr.attr, &sensor_dev_attr_fan3_airflow.dev_attr.attr,

View File

@ -24,7 +24,7 @@ except ImportError as e:
MAX_S6000_FANTRAY = 3 MAX_S6000_FANTRAY = 3
MAX_S6000_PSU = 2 MAX_S6000_PSU = 2
MAX_S6000_THERMAL = 10 MAX_S6000_THERMAL = 6
MAX_S6000_COMPONENT = 4 MAX_S6000_COMPONENT = 4
@ -44,6 +44,8 @@ class Chassis(ChassisBase):
def __init__(self): def __init__(self):
ChassisBase.__init__(self) ChassisBase.__init__(self)
self.status_led_reg = "system_led"
self.supported_led_color = ['green', 'blinking green', 'amber', 'blinking amber']
# Initialize SFP list # Initialize SFP list
self.PORT_START = 0 self.PORT_START = 0
self.PORT_END = 31 self.PORT_END = 31
@ -101,13 +103,30 @@ class Chassis(ChassisBase):
try: try:
with open(mb_reg_file, 'r') as fd: with open(mb_reg_file, 'r') as fd:
rv = fd.read() rv = fd.read()
except Exception as error: except IOError:
rv = 'ERR' rv = 'ERR'
rv = rv.rstrip('\r\n') rv = rv.rstrip('\r\n')
rv = rv.lstrip(" ") rv = rv.lstrip(" ")
return rv 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): def _nvram_write(self, offset, val):
resource = "/dev/nvram" resource = "/dev/nvram"
fd = os.open(resource, os.O_RDWR) fd = os.open(resource, os.O_RDWR)
@ -179,6 +198,23 @@ class Chassis(ChassisBase):
""" """
return True 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): def get_base_mac(self):
""" """
Retrieves the base MAC address for the chassis Retrieves the base MAC address for the chassis
@ -305,4 +341,41 @@ class Chassis(ChassisBase):
return True, ret_dict return True, ret_dict
return False, 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

View File

@ -29,8 +29,8 @@ class Component(ComponentBase):
"booting")], "booting")],
["System-CPLD", "Used for managing CPU board devices and power"], ["System-CPLD", "Used for managing CPU board devices and power"],
["Master-CPLD", ("Used for managing Fan, PSU, system LEDs, QSFP " ["Master-CPLD", ("Used for managing Fan, PSU, system LEDs, QSFP "
"modules (1-16)")], "modules (17-32)")],
["Slave-CPLD", "Used for managing QSFP modules (17-32)"] ["Slave-CPLD", "Used for managing QSFP modules (1-16)"]
] ]
def __init__(self, component_index): def __init__(self, component_index):
@ -90,6 +90,55 @@ class Component(ComponentBase):
""" """
return self.name 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): def get_description(self):
""" """
Retrieves the description of the component Retrieves the description of the component

View File

@ -36,8 +36,8 @@ psu_eeprom_format = [
('Fab Rev', 's', 2) ('Fab Rev', 's', 2)
] ]
# Fan eeprom fields in format required by EepromDecoder # FanTray eeprom fields in format required by EepromDecoder
fan_eeprom_format = [ fantray_eeprom_format = [
('PPID', 's', 20), ('DPN Rev', 's', 3), ('Service Tag', 's', 7), ('PPID', 's', 20), ('DPN Rev', 's', 3), ('Service Tag', 's', 7),
('Part Number', 's', 10), ('Part Num Revision', 's', 3), ('Part Number', 's', 10), ('Part Num Revision', 's', 3),
('Mfg Test', 's', 2), ('Redundant copy', 's', 83), ('Mfg Test', 's', 2), ('Redundant copy', 's', 83),
@ -51,10 +51,10 @@ class Eeprom(TlvInfoDecoder):
I2C_DIR = "/sys/class/i2c-adapter/" 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_psu_eeprom = is_psu
self.is_fan_eeprom = is_fan self.is_fantray_eeprom = is_fantray
self.is_sys_eeprom = not (is_psu | is_fan) self.is_sys_eeprom = not (is_psu | is_fantray)
if self.is_sys_eeprom: if self.is_sys_eeprom:
self.start_offset = 0 self.start_offset = 0
@ -71,10 +71,10 @@ class Eeprom(TlvInfoDecoder):
+ "i2c-1/1-005{}/eeprom".format(2 - self.index) + "i2c-1/1-005{}/eeprom".format(2 - self.index)
self.format = psu_eeprom_format self.format = psu_eeprom_format
else: else:
self.index = fan_index self.index = fantray_index
self.eeprom_path = self.I2C_DIR \ self.eeprom_path = self.I2C_DIR \
+ "i2c-11/11-005{}/eeprom".format(4 - self.index) + "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, EepromDecoder.__init__(self, self.eeprom_path, self.format,
self.start_offset, '', True) self.start_offset, '', True)
self._load_device_eeprom() self._load_device_eeprom()

View File

@ -13,7 +13,6 @@ try:
import os import os
import glob import glob
from sonic_platform_base.fan_base import FanBase from sonic_platform_base.fan_base import FanBase
from sonic_platform.eeprom import Eeprom
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -25,33 +24,37 @@ MAX_S6000_FAN_SPEED = 19000
class Fan(FanBase): class Fan(FanBase):
"""DellEMC Platform-specific Fan class""" """DellEMC Platform-specific Fan class"""
CPLD_DIR = "/sys/devices/platform/dell-s6000-cpld.0/"
I2C_DIR = "/sys/class/i2c-adapter/" 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_psu_fan = psu_fan
self.is_driver_initialized = True self.is_driver_initialized = True
if not self.is_psu_fan: if not self.is_psu_fan:
# Fan is 1-based in DellEMC platforms # Fan is 1-based in DellEMC platforms
self.index = fan_index + 1 self.fantray_index = fantray_index
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.index = fan_index self.index = fan_index
self.dependency = dependency 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 +\ 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 +\ 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: try:
hwmon_node = os.listdir(hwmon_dir)[0] hwmon_node = os.listdir(hwmon_dir)[0]
except OSError: except OSError:
@ -61,43 +64,6 @@ class Fan(FanBase):
self.get_fan_speed_reg = hwmon_dir + hwmon_node + '/fan1_input' self.get_fan_speed_reg = hwmon_dir + hwmon_node + '/fan1_input'
self.max_fan_speed = MAX_S6000_PSU_FAN_SPEED 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): def _get_i2c_register(self, reg_file):
# On successful read, returns the value read from given # On successful read, returns the value read from given
# reg_name and on failure returns 'ERR' # reg_name and on failure returns 'ERR'
@ -155,9 +121,9 @@ class Fan(FanBase):
string: The name of the Fan string: The name of the Fan
""" """
if not self.is_psu_fan: if not self.is_psu_fan:
return "FanTray{}-Fan1".format(self.index) return "FanTray{}-Fan{}".format(self.fantray_index, self.index)
else: else:
return "PSU{} Fan".format(self.index) return "PSU{} Fan".format(self.psu_index)
def get_presence(self): def get_presence(self):
""" """
@ -166,42 +132,23 @@ class Fan(FanBase):
Returns: Returns:
bool: True if Fan is present, False if not bool: True if Fan is present, False if not
""" """
status = False return self.dependency.get_presence()
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
def get_model(self): def get_model(self):
""" """
Retrieves the part number of the Fan Retrieves the part number of the Fan
Returns: Returns:
string: Part number of Fan string: Part number of Fan
""" """
if not self.is_psu_fan: return 'NA'
return self.eeprom.get_part_number()
else:
return 'NA'
def get_serial(self): def get_serial(self):
""" """
Retrieves the serial number of the Fan Retrieves the serial number of the Fan
Returns: Returns:
string: Serial number of Fan string: Serial number of Fan
""" """
# Sample Serial number format "US-01234D-54321-25A-0123-A00" return 'NA'
if not self.is_psu_fan:
return self.eeprom.get_serial_number()
else:
return 'NA'
def get_status(self): def get_status(self):
""" """
@ -218,6 +165,23 @@ class Fan(FanBase):
return 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 Fan is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_direction(self): def get_direction(self):
""" """
Retrieves the fan airflow direction Retrieves the fan airflow direction
@ -234,11 +198,10 @@ class Fan(FanBase):
if self.is_psu_fan: if self.is_psu_fan:
direction = {1: self.FAN_DIRECTION_EXHAUST, 2: self.FAN_DIRECTION_INTAKE, direction = {1: self.FAN_DIRECTION_EXHAUST, 2: self.FAN_DIRECTION_INTAKE,
3: self.FAN_DIRECTION_EXHAUST, 4: self.FAN_DIRECTION_INTAKE} 3: self.FAN_DIRECTION_EXHAUST, 4: self.FAN_DIRECTION_INTAKE}
fan_direction = self.dependency.eeprom.airflow_fan_type()
else: else:
direction = {1: self.FAN_DIRECTION_EXHAUST, 2: self.FAN_DIRECTION_INTAKE} 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) return direction.get(fan_direction, self.FAN_DIRECTION_NOT_APPLICABLE)
def get_speed(self): def get_speed(self):
@ -282,8 +245,8 @@ class Fan(FanBase):
Returns: Returns:
bool: True if set success, False if fail. bool: True if set success, False if fail.
""" """
fan_set = (speed * self.max_fan_speed)/ 100 fan_set = (speed * self.max_fan_speed) // 100
rv = self._set_i2c_register(self.set_fan_speed_reg , fan_set) rv = self._set_i2c_register(self.set_fan_speed_reg, fan_set)
if (rv != 'ERR'): if (rv != 'ERR'):
return True return True
else: else:
@ -298,16 +261,9 @@ class Fan(FanBase):
Returns: Returns:
bool: True if set success, False if fail. bool: True if set success, False if fail.
""" """
if self.is_psu_fan or (color not in self.supported_led_color): # No LED available for FanTray and PSU Fan
return False # Return True to avoid thermalctld alarm.
if(color == self.STATUS_LED_COLOR_AMBER): return True
color = 'yellow'
rv = self._set_cpld_register(self.fan_led_reg ,color)
if (rv != 'ERR'):
return True
else:
return False
def get_status_led(self): def get_status_led(self):
""" """
@ -316,18 +272,8 @@ class Fan(FanBase):
Returns: Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings. A string, one of the predefined STATUS_LED_COLOR_* strings.
""" """
if self.is_psu_fan: # No LED available for FanTray and PSU Fan
# No LED available for PSU Fan return None
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
def get_target_speed(self): def get_target_speed(self):
""" """

View File

@ -9,26 +9,176 @@
######################################################################## ########################################################################
try: try:
import os
from sonic_platform_base.fan_drawer_base import FanDrawerBase from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.eeprom import Eeprom
from sonic_platform.fan import Fan from sonic_platform.fan import Fan
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
MAX_S6000_FANS_PER_FANTRAY = 2
class FanDrawer(FanDrawerBase): 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): def __init__(self, fantray_index):
FanDrawerBase.__init__(self) FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms # FanTray is 1-based in DellEMC platforms
self.fantrayindex = fantray_index + 1 self.index = fantray_index + 1
self._fan_list.append(Fan(fantray_index)) 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): def get_name(self):
""" """
Retrieves the fan drawer name Retrieves the Fandrawer name
Returns: Returns:
string: The name of the device 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

View File

@ -15,9 +15,12 @@ try:
from sonic_platform_base.psu_base import PsuBase from sonic_platform_base.psu_base import PsuBase
from sonic_platform.eeprom import Eeprom from sonic_platform.eeprom import Eeprom
from sonic_platform.fan import Fan from sonic_platform.fan import Fan
from sonic_platform.thermal import Thermal
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
MAX_S6000_THERMALS_PER_PSU = 2
class Psu(PsuBase): class Psu(PsuBase):
"""DellEMC Platform-specific PSU class""" """DellEMC Platform-specific PSU class"""
@ -53,7 +56,10 @@ class Psu(PsuBase):
self.eeprom = Eeprom(is_psu=True, psu_index=self.index) 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): def _get_cpld_register(self, reg_name):
# On successful read, returns the value read from given # On successful read, returns the value read from given
@ -171,6 +177,23 @@ class Psu(PsuBase):
return 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 PSU is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True
def get_voltage(self): def get_voltage(self):
""" """
Retrieves current PSU voltage output Retrieves current PSU voltage output
@ -266,3 +289,50 @@ class Psu(PsuBase):
# In S6000, the firmware running in the PSU controls the LED # In S6000, the firmware running in the PSU controls the LED
# and the PSU LED state cannot be changed from CPU. # and the PSU LED state cannot be changed from CPU.
return False 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

View File

@ -10,6 +10,7 @@
try: try:
import re import re
import struct
import time import time
from sonic_platform_base.sfp_base import SfpBase from sonic_platform_base.sfp_base import SfpBase
from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId
@ -27,6 +28,9 @@ INFO_OFFSET = 128
DOM_OFFSET = 0 DOM_OFFSET = 0
DOM_OFFSET1 = 384 DOM_OFFSET1 = 384
QSFP_CONTROL_OFFSET = 86
QSFP_POWEROVERRIDE_OFFSET = 93
cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)', cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)',
'Length OM1(m)', 'Length Cable Assembly(m)') 'Length OM1(m)', 'Length Cable Assembly(m)')
@ -108,7 +112,7 @@ class Sfp(SfpBase):
sfp_control, sfp_ctrl_idx): sfp_control, sfp_ctrl_idx):
SfpBase.__init__(self) SfpBase.__init__(self)
self.sfp_type = sfp_type self.sfp_type = sfp_type
self.index = index self.index = index + 1
self.eeprom_path = eeprom_path self.eeprom_path = eeprom_path
self.sfp_control = sfp_control self.sfp_control = sfp_control
self.sfp_ctrl_idx = sfp_ctrl_idx self.sfp_ctrl_idx = sfp_ctrl_idx
@ -132,9 +136,10 @@ class Sfp(SfpBase):
eeprom.close() eeprom.close()
return None return None
raw = bytearray(raw)
try: try:
for n in range(0, num_bytes): 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: except BaseException:
eeprom.close() eeprom.close()
return None return None
@ -588,40 +593,14 @@ class Sfp(SfpBase):
""" """
Retrieves the TX disabled channels in this SFP Retrieves the TX disabled channels in this SFP
""" """
tx_disable = None tx_disable_channel = 0
tx_disable_list = []
tx_disable_data = self._get_eeprom_data('tx_disable') tx_disable = self.get_tx_disable()
if (tx_disable_data is not None): for channel, disable in enumerate(tx_disable):
tx_disable = tx_disable_data['data']['Tx1Disable']['value'] if disable:
if (tx_disable == 'On'): tx_disable_channel |= 1 << channel
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)
bit4 = int(tx_disable_list[3]) * 8 return tx_disable_channel
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
def get_lpmode(self): def get_lpmode(self):
""" """
@ -722,7 +701,6 @@ class Sfp(SfpBase):
return rx_power_list return rx_power_list
def get_tx_power(self): def get_tx_power(self):
""" """
Retrieves the TX power of this SFP Retrieves the TX power of this SFP
@ -822,19 +800,68 @@ class Sfp(SfpBase):
""" """
Disable SFP TX for all channels 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): def tx_disable_channel(self, channel, disable):
""" """
Sets the tx_disable for specified SFP channels 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): def set_power_override(self, power_override, power_set):
""" """
Sets SFP power level using power_override and 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): def get_status(self):
""" """
@ -848,3 +875,20 @@ class Sfp(SfpBase):
status = True status = True
return 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 device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True

View File

@ -13,7 +13,6 @@ try:
import os import os
import glob import glob
from sonic_platform_base.thermal_base import ThermalBase from sonic_platform_base.thermal_base import ThermalBase
from sonic_platform.psu import Psu
except ImportError as e: except ImportError as e:
raise ImportError(str(e) + "- required module not found") raise ImportError(str(e) + "- required module not found")
@ -34,11 +33,20 @@ class Thermal(ThermalBase):
'PSU1-Sensor 1', 'PSU1-Sensor 2', 'PSU2-Sensor 1', 'PSU1-Sensor 1', 'PSU1-Sensor 2', 'PSU2-Sensor 1',
'PSU2-Sensor 2', 'CPU Core 0', 'CPU Core 1') 'PSU2-Sensor 2', 'CPU Core 0', 'CPU Core 1')
def __init__(self, thermal_index): def __init__(self, thermal_index,
self.index = thermal_index + 1 psu_index=1, psu_thermal=False, dependency=None):
self.is_psu_thermal = False self.is_psu_thermal = psu_thermal
self.dependency = dependency
self.is_driver_initialized = True 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: if self.index < 9:
i2c_path = self.I2C_DIR + self.I2C_DEV_MAPPING[self.index - 1][0] i2c_path = self.I2C_DIR + self.I2C_DEV_MAPPING[self.index - 1][0]
@ -54,10 +62,6 @@ class Thermal(ThermalBase):
if self.index == 4: if self.index == 4:
hwmon_temp_suffix = "crit" hwmon_temp_suffix = "crit"
if self.index > 4:
self.is_psu_thermal = True
self.dependency = Psu(self.index / 7)
else: else:
dev_path = "/sys/devices/platform/coretemp.0/hwmon/" dev_path = "/sys/devices/platform/coretemp.0/hwmon/"
hwmon_temp_index = self.index - 7 hwmon_temp_index = self.index - 7
@ -168,6 +172,23 @@ class Thermal(ThermalBase):
else: else:
return True 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): def get_temperature(self):
""" """
Retrieves current temperature reading from thermal Retrieves current temperature reading from thermal

View File

@ -54,9 +54,21 @@ class Chassis(ChassisBase):
power_reason_dict[33] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC power_reason_dict[33] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC
power_reason_dict[44] = ChassisBase.REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED 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): def __init__(self):
ChassisBase.__init__(self) ChassisBase.__init__(self)
self.status_led_reg = "sys_status_led"
self.supported_led_color = ['green', 'blinking green', 'amber', 'blinking amber']
# Initialize EEPROM # Initialize EEPROM
self._eeprom = Eeprom() self._eeprom = Eeprom()
for i in range(MAX_S6100_MODULE): for i in range(MAX_S6100_MODULE):
@ -113,6 +125,23 @@ class Chassis(ChassisBase):
rv = rv.lstrip(" ") rv = rv.lstrip(" ")
return rv 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): def _get_register(self, reg_file):
# On successful read, returns the value read from given # On successful read, returns the value read from given
# reg_name and on failure returns 'ERR' # reg_name and on failure returns 'ERR'
@ -172,6 +201,23 @@ class Chassis(ChassisBase):
""" """
return True 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): def get_base_mac(self):
""" """
Retrieves the base MAC address for the chassis Retrieves the base MAC address for the chassis
@ -325,3 +371,38 @@ class Chassis(ChassisBase):
break break
return True, ret_dict 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

View File

@ -39,9 +39,10 @@ class Component(ComponentBase):
] ]
def __init__(self, component_index=0, 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.is_module_component = is_module
self.dependency = dependency
if self.is_module_component: if self.is_module_component:
self.index = iom_index self.index = iom_index
@ -132,6 +133,61 @@ class Component(ComponentBase):
""" """
return self.name 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): def get_description(self):
""" """
Retrieves the description of the component Retrieves the description of the component

View File

@ -26,30 +26,23 @@ class Fan(FanBase):
HWMON_NODE = os.listdir(HWMON_DIR)[0] HWMON_NODE = os.listdir(HWMON_DIR)[0]
MAILBOX_DIR = HWMON_DIR + HWMON_NODE 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 self.is_psu_fan = psu_fan
if not self.is_psu_fan: if not self.is_psu_fan:
# API index is starting from 0, DellEMC platform index is starting self.fantrayindex = fantray_index
# from 1 self.dependency = dependency
self.fantrayindex = fantray_index + 1
self.fanindex = fan_index + 1
self.fan_presence_reg = "fan{}_fault".format(
2 * self.fantrayindex - 1)
self.fan_status_reg = "fan{}_alarm".format( self.fan_status_reg = "fan{}_alarm".format(
2 * self.fantrayindex - 1) 2 * self.fantrayindex - 1)
self.get_fan_speed_reg = "fan{}_input".format( self.get_fan_speed_reg = "fan{}_input".format(
2 * self.fantrayindex - 1) 2 * self.fantrayindex - 1)
self.get_fan_dir_reg = "fan{}_airflow".format( self.get_fan_dir_reg = "fan{}_airflow".format(
2 * self.fantrayindex - 1) 2 * self.fantrayindex - 1)
self.fan_serialno_reg = "fan{}_serialno".format(
2 * self.fantrayindex - 1)
self.max_fan_speed = MAX_S6100_FAN_SPEED self.max_fan_speed = MAX_S6100_FAN_SPEED
else: else:
# PSU Fan index starts from 11 self.psuindex = psu_index
self.fanindex = fan_index + 10 self.fan_presence_reg = "fan{}_fault".format(self.psuindex + 10)
self.fan_presence_reg = "fan{}_fault".format(self.fanindex) self.get_fan_speed_reg = "fan{}_input".format(self.psuindex + 10)
self.get_fan_speed_reg = "fan{}_input".format(self.fanindex) self.get_fan_dir_reg = "fan{}_airflow".format(self.psuindex + 10)
self.get_fan_dir_reg = "fan{}_airflow".format(self.fanindex)
self.max_fan_speed = MAX_S6100_PSU_FAN_SPEED self.max_fan_speed = MAX_S6100_PSU_FAN_SPEED
def _get_pmc_register(self, reg_name): def _get_pmc_register(self, reg_name):
@ -77,10 +70,9 @@ class Fan(FanBase):
string: The name of the device string: The name of the device
""" """
if not self.is_psu_fan: if not self.is_psu_fan:
return "FanTray{}-Fan{}".format( return "FanTray{}-Fan1".format(self.fantrayindex)
self.fantrayindex, self.fanindex - 1)
else: else:
return "PSU{} Fan".format(self.fanindex - 10) return "PSU{} Fan".format(self.psuindex)
def get_model(self): def get_model(self):
""" """
@ -88,21 +80,7 @@ class Fan(FanBase):
Returns: Returns:
string: Part number of FAN string: Part number of FAN
""" """
# For Serial number "US-01234D-54321-25A-0123-A00", the part return 'NA'
# 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
def get_serial(self): def get_serial(self):
""" """
@ -110,15 +88,7 @@ class Fan(FanBase):
Returns: Returns:
string: Serial number of FAN string: Serial number of FAN
""" """
# Sample Serial number format "US-01234D-54321-25A-0123-A00" return 'NA'
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
def get_presence(self): def get_presence(self):
""" """
@ -126,14 +96,17 @@ class Fan(FanBase):
Returns: Returns:
bool: True if fan is present, False if not bool: True if fan is present, False if not
""" """
status = False if not self.is_psu_fan:
fantray_presence = self._get_pmc_register(self.fan_presence_reg) return self.dependency.get_presence()
if (fantray_presence != 'ERR'):
fantray_presence = int(fantray_presence, 10)
if (~fantray_presence & 0b1):
status = True
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): def get_status(self):
""" """
@ -157,6 +130,23 @@ class Fan(FanBase):
return 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 1
def is_replaceable(self):
"""
Indicate whether Fan is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_direction(self): def get_direction(self):
""" """
Retrieves the fan airflow direction Retrieves the fan airflow direction
@ -216,7 +206,6 @@ class Fan(FanBase):
Returns: Returns:
bool: True if set success, False if fail. bool: True if set success, False if fail.
""" """
# Fan speeds are controlled by Smart-fussion FPGA. # Fan speeds are controlled by Smart-fussion FPGA.
return False return False
@ -229,7 +218,7 @@ class Fan(FanBase):
Returns: Returns:
bool: True if set success, False if fail. 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 to avoid thermalctld alarm.
return True return True
@ -240,17 +229,8 @@ class Fan(FanBase):
Returns: Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings. A string, one of the predefined STATUS_LED_COLOR_* strings.
""" """
if self.is_psu_fan: # No LED available for FanTray and PSU Fan
# No LED available for PSU Fan return None
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
def get_target_speed(self): def get_target_speed(self):
""" """
@ -262,5 +242,3 @@ class Fan(FanBase):
# Fan speeds are controlled by Smart-fussion FPGA. # Fan speeds are controlled by Smart-fussion FPGA.
# Return current speed to avoid false thermalctld alarm. # Return current speed to avoid false thermalctld alarm.
return self.get_speed() return self.get_speed()

View File

@ -9,6 +9,8 @@
######################################################################## ########################################################################
try: try:
import os
from sonic_platform_base.fan_drawer_base import FanDrawerBase from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.fan import Fan from sonic_platform.fan import Fan
except ImportError as e: except ImportError as e:
@ -16,14 +18,38 @@ except ImportError as e:
class FanDrawer(FanDrawerBase): 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): def __init__(self, fantray_index):
FanDrawerBase.__init__(self) FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms # FanTray is 1-based in DellEMC platforms
self.fantrayindex = fantray_index + 1 self.index = fantray_index + 1
self._fan_list.append(Fan(fantray_index)) 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): def get_name(self):
""" """
@ -31,4 +57,105 @@ class FanDrawer(FanDrawerBase):
Returns: Returns:
string: The name of the device 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

View File

@ -63,7 +63,7 @@ class Module(ModuleBase):
self.iom_presence_reg = "iom_presence" self.iom_presence_reg = "iom_presence"
component = Component(is_module=True, iom_index=self.index, 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) self._component_list.append(component)
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/i2c-{1}/{1}-0050/eeprom" eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/i2c-{1}/{1}-0050/eeprom"
@ -157,6 +157,23 @@ class Module(ModuleBase):
return 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 Module is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True
def get_base_mac(self): def get_base_mac(self):
""" """
Retrieves the base MAC address for the module Retrieves the base MAC address for the module

View File

@ -34,13 +34,15 @@ class Psu(PsuBase):
self.psu_voltage_reg = "in30_input" self.psu_voltage_reg = "in30_input"
self.psu_current_reg = "curr602_input" self.psu_current_reg = "curr602_input"
self.psu_power_reg = "power2_input" self.psu_power_reg = "power2_input"
self.psu_temperature_reg = "temp14_input"
elif self.index == 2: elif self.index == 2:
self.psu_voltage_reg = "in32_input" self.psu_voltage_reg = "in32_input"
self.psu_current_reg = "curr702_input" self.psu_current_reg = "curr702_input"
self.psu_power_reg = "power4_input" self.psu_power_reg = "power4_input"
self.psu_temperature_reg = "temp15_input"
# Passing True to specify it is a PSU fan # 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) self._fan_list.append(psu_fan)
def _get_pmc_register(self, reg_name): 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 # In S6100, SmartFusion FPGA controls the PSU LED and the PSU
# LED state cannot be changed from CPU. # LED state cannot be changed from CPU.
return False 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

View File

@ -10,6 +10,7 @@
try: try:
import re import re
import struct
import time import time
from sonic_platform_base.sfp_base import SfpBase from sonic_platform_base.sfp_base import SfpBase
from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId from sonic_platform_base.sonic_sfp.sff8436 import sff8436InterfaceId
@ -27,6 +28,9 @@ INFO_OFFSET = 128
DOM_OFFSET = 0 DOM_OFFSET = 0
DOM_OFFSET1 = 384 DOM_OFFSET1 = 384
QSFP_CONTROL_OFFSET = 86
QSFP_POWEROVERRIDE_OFFSET = 93
cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)', cable_length_tup = ('Length(km)', 'Length OM3(2m)', 'Length OM2(m)',
'Length OM1(m)', 'Length Cable Assembly(m)') 'Length OM1(m)', 'Length Cable Assembly(m)')
@ -107,7 +111,7 @@ class Sfp(SfpBase):
sfp_control, sfp_ctrl_idx): sfp_control, sfp_ctrl_idx):
SfpBase.__init__(self) SfpBase.__init__(self)
self.sfp_type = sfp_type self.sfp_type = sfp_type
self.index = index self.index = index + 1
self.eeprom_path = eeprom_path self.eeprom_path = eeprom_path
self.sfp_control = sfp_control self.sfp_control = sfp_control
self.sfp_ctrl_idx = sfp_ctrl_idx self.sfp_ctrl_idx = sfp_ctrl_idx
@ -131,9 +135,10 @@ class Sfp(SfpBase):
eeprom.close() eeprom.close()
return None return None
raw = bytearray(raw)
try: try:
for n in range(0, num_bytes): 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: except BaseException:
eeprom.close() eeprom.close()
return None return None
@ -594,40 +599,14 @@ class Sfp(SfpBase):
""" """
Retrieves the TX disabled channels in this SFP Retrieves the TX disabled channels in this SFP
""" """
tx_disable = None tx_disable_channel = 0
tx_disable_list = []
tx_disable_data = self._get_eeprom_data('tx_disable') tx_disable = self.get_tx_disable()
if (tx_disable_data is not None): for channel, disable in enumerate(tx_disable):
tx_disable = tx_disable_data['data']['Tx1Disable']['value'] if disable:
if (tx_disable == 'On'): tx_disable_channel |= 1 << channel
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)
bit4 = int(tx_disable_list[3]) * 8 return tx_disable_channel
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
def get_lpmode(self): def get_lpmode(self):
""" """
@ -836,19 +815,68 @@ class Sfp(SfpBase):
""" """
Disable SFP TX for all channels 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): def tx_disable_channel(self, channel, disable):
""" """
Sets the tx_disable for specified SFP channels 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): def set_power_override(self, power_override, power_set):
""" """
Sets SFP power level using power_override and 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): def get_status(self):
""" """
@ -862,3 +890,20 @@ class Sfp(SfpBase):
status = True status = True
return 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 device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True

View File

@ -131,6 +131,23 @@ class Thermal(ThermalBase):
return 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 Thermal is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_temperature(self): def get_temperature(self):
""" """
Retrieves current temperature reading from thermal Retrieves current temperature reading from thermal