platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py (111 lines of code) (raw):
#!/usr/bin/env python
########################################################################
# DellEMC S6100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fans' information which are available in the platform.
#
########################################################################
import os.path
try:
from sonic_platform_base.fan_base import FanBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
MAX_S6100_PSU_FAN_SPEED = 18000
MAX_S6100_FAN_SPEED = 16000
class Fan(FanBase):
"""DellEMC Platform-specific Fan 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=1, psu_index=1, psu_fan=False, dependency=None):
FanBase.__init__(self)
self.is_psu_fan = psu_fan
if not self.is_psu_fan:
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.max_fan_speed = MAX_S6100_FAN_SPEED
else:
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):
# 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):
"""
Retrieves the fan name
Returns:
string: The name of the device
"""
if not self.is_psu_fan:
return "FanTray{}-Fan1".format(self.fantrayindex)
else:
return "PSU{} Fan".format(self.psuindex)
def get_model(self):
"""
Retrieves the part number of the FAN
Returns:
string: Part number of FAN
"""
return 'NA'
def get_serial(self):
"""
Retrieves the serial number of the FAN
Returns:
string: Serial number of FAN
"""
return 'NA'
def get_presence(self):
"""
Retrieves the presence of the FAN
Returns:
bool: True if fan is present, False if not
"""
if not self.is_psu_fan:
return self.dependency.get_presence()
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):
"""
Retrieves the operational status of the FAN
Returns:
bool: True if FAN is operating properly, False if not
"""
status = False
if self.is_psu_fan:
fantray_status = self._get_pmc_register(self.get_fan_speed_reg)
if (fantray_status != 'ERR'):
fantray_status = int(fantray_status, 10)
if (fantray_status > 1000):
status = True
else:
fantray_status = self._get_pmc_register(self.fan_status_reg)
if (fantray_status != 'ERR'):
fantray_status = int(fantray_status, 10)
if (~fantray_status & 0b1):
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 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
Returns:
A string, either FAN_DIRECTION_INTAKE or FAN_DIRECTION_EXHAUST
depending on fan direction
Notes:
In DellEMC platforms,
- Forward/Exhaust : Air flows from Port side to Fan side.
- Reverse/Intake : Air flows from Fan side to Port side.
"""
direction = [self.FAN_DIRECTION_INTAKE, self.FAN_DIRECTION_EXHAUST]
fan_direction = self._get_pmc_register(self.get_fan_dir_reg)
if (fan_direction != 'ERR') and self.get_presence():
fan_direction = int(fan_direction, 10)
else:
return self.FAN_DIRECTION_NOT_APPLICABLE
return direction[fan_direction]
def get_speed(self):
"""
Retrieves the speed of fan
Returns:
int: percentage of the max fan speed
"""
fan_speed = self._get_pmc_register(self.get_fan_speed_reg)
if (fan_speed != 'ERR') and self.get_presence():
speed_in_rpm = int(fan_speed, 10)
speed = (100 * speed_in_rpm)//self.max_fan_speed
else:
speed = 0
return speed
def get_speed_tolerance(self):
"""
Retrieves the speed tolerance of the fan
Returns:
An integer, the percentage of variance from target speed which is
considered tolerable
"""
if self.get_presence():
# The tolerance value is fixed as 20% for all the DellEmc platform
tolerance = 20
else:
tolerance = 0
return tolerance
def set_speed(self, speed):
"""
Set fan speed to expected value
Args:
speed: An integer, the percentage of full fan speed to set fan to,
in the range 0 (off) to 100 (full speed)
Returns:
bool: True if set success, False if fail.
"""
# Fan speeds are controlled by Smart-fussion FPGA.
return False
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.
"""
# No LED available for FanTray and PSU Fan
# 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.
"""
# No LED available for FanTray and PSU Fan
return None
def get_target_speed(self):
"""
Retrieves the target (expected) speed of the fan
Returns:
An integer, the percentage of full fan speed, in the range 0 (off)
to 100 (full speed)
"""
# Fan speeds are controlled by Smart-fussion FPGA.
# Return current speed to avoid false thermalctld alarm.
return self.get_speed()