Skip to content

DellEMC Z9100 : Platform2.0 API implementation [PSU, Thermal] #3361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__all__ = ["platform", "chassis", "sfp"]
__all__ = ["platform", "chassis", "fan", "psu", "sfp", "thermal"]
from sonic_platform import *

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.sfp import Sfp
from sonic_platform.fan import Fan
from sonic_platform.psu import Psu
from sonic_platform.thermal import Thermal
from eeprom import Eeprom
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
Expand All @@ -23,6 +25,7 @@
MAX_Z9100_FANTRAY = 5
MAX_Z9100_FAN = 2
MAX_Z9100_PSU = 2
MAX_Z9100_THERMAL = 8

BIOS_QUERY_VERSION_COMMAND = "dmidecode -s system-version"
#components definitions
Expand Down Expand Up @@ -104,6 +107,14 @@ def __init__(self):
fan = Fan(i, j)
self._fan_list.append(fan)

for i in range(MAX_Z9100_PSU):
psu = Psu(i)
self._psu_list.append(psu)

for i in range(MAX_Z9100_THERMAL):
thermal = Thermal(i)
self._thermal_list.append(thermal)

# Initialize component list
self._component_name_list.append(COMPONENT_BIOS)
self._component_name_list.append(SWITCH_CPLD1)
Expand Down Expand Up @@ -191,6 +202,17 @@ def get_serial_number(self):
"""
return self.sys_eeprom.serial_number_str()

def get_system_eeprom_info(self):
"""
Retrieves the full content of system EEPROM information for the chassis

Returns:
A dictionary where keys are the type code defined in
OCP ONIE TlvInfo EEPROM format and values are their corresponding
values.
"""
return self.sys_eeprom.system_eeprom_info()

def get_reboot_cause(self):
"""
Retrieves the cause of the previous reboot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,42 @@ class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self):
self.eeprom_path = "/sys/class/i2c-adapter/i2c-2/2-0050/eeprom"
super(Eeprom, self).__init__(self.eeprom_path, 0, '', True)
self.eeprom_tlv_dict = dict()
try:
self.eeprom_data = self.read_eeprom()
except:
self.eeprom_data = "N/A"
raise RuntimeError("Eeprom is not Programmed")
else:
eeprom = self.eeprom_data

if not self.is_valid_tlvinfo_header(eeprom):
return

total_length = (ord(eeprom[9]) << 8) | ord(eeprom[10])
tlv_index = self._TLV_INFO_HDR_LEN
tlv_end = self._TLV_INFO_HDR_LEN + total_length

while (tlv_index + 2) < len(eeprom) and tlv_index < tlv_end:
if not self.is_valid_tlv(eeprom[tlv_index:]):
break

tlv = eeprom[tlv_index:tlv_index + 2
+ ord(eeprom[tlv_index + 1])]
code = "0x%02X" % (ord(tlv[0]))

if ord(tlv[0]) == self._TLV_CODE_VENDOR_EXT:
value = str((ord(tlv[2]) << 24) | (ord(tlv[3]) << 16) |
(ord(tlv[4]) << 8) | ord(tlv[5]))
value += str(tlv[6:6 + ord(tlv[1])])
else:
name, value = self.decoder(None, tlv)

self.eeprom_tlv_dict[code] = value
if ord(eeprom[tlv_index]) == self._TLV_CODE_CRC_32:
break

tlv_index += ord(eeprom[tlv_index+1]) + 2

def serial_number_str(self):
(is_valid, results) = self.get_tlv_field(
Expand Down Expand Up @@ -74,3 +105,10 @@ def revision_str(self):

return results[2]

def system_eeprom_info(self):
"""
Returns a dictionary, where keys are the type code defined in
ONIE EEPROM format and values are their corresponding values
found in the system EEPROM.
"""
return self.eeprom_tlv_dict
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Fan(FanBase):
HWMON_NODE = os.listdir(HWMON_DIR)[0]
MAILBOX_DIR = HWMON_DIR + HWMON_NODE

def __init__(self, fantray_index, fan_index=1, psu_fan=False):
def __init__(self, fantray_index=1, fan_index=1, psu_fan=False):
self.is_psu_fan = psu_fan
if not self.is_psu_fan:
# API index is starting from 0, DellEMC platform index is starting
Expand Down Expand Up @@ -73,7 +73,7 @@ def get_name(self):
if not self.is_psu_fan:
return "FanTray{}-Fan{}".format(self.fantrayindex, self.fanindex)
else:
return "PSU{} Fan".format(self.index - 10)
return "PSU{} Fan".format(self.fanindex - 10)

def get_model(self):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
#!/usr/bin/env python

########################################################################
# DellEMC Z9100
#
# Module contains an implementation of SONiC Platform Base API and
# provides the PSUs' information which are available in the platform
#
########################################################################


try:
import os
from sonic_platform_base.psu_base import PsuBase
from sonic_platform.fan import Fan
except ImportError as e:
raise ImportError(str(e) + "- required module not found")


class Psu(PsuBase):
"""DellEMC Platform-specific PSU 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, psu_index):
# PSU is 1-based in DellEMC platforms
self.index = psu_index + 1
self.psu_presence_reg = "psu{}_presence".format(self.index)
self.psu_serialno_reg = "psu{}_serialno".format(self.index)
if self.index == 1:
self.psu_voltage_reg = "in30_input"
self.psu_current_reg = "curr602_input"
self.psu_power_reg = "power2_input"
elif self.index == 2:
self.psu_voltage_reg = "in32_input"
self.psu_current_reg = "curr702_input"
self.psu_power_reg = "power4_input"

# Overriding _fan_list class variable defined in PsuBase, to
# make it unique per Psu object
self._fan_list = []

# Passing True to specify it is a PSU fan
psu_fan = Fan(fan_index=self.index, psu_fan=True)
self._fan_list.append(psu_fan)

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 name of the device

Returns:
string: The name of the device
"""
return "PSU{}".format(self.index)

def get_presence(self):
"""
Retrieves the presence of the Power Supply Unit (PSU)

Returns:
bool: True if PSU is present, False if not
"""
status = False
psu_presence = self._get_pmc_register(self.psu_presence_reg)
if (psu_presence != 'ERR'):
psu_presence = int(psu_presence, 16)
# Checking whether bit 0 is not set
if (~psu_presence & 0b1):
status = True

return status

def get_model(self):
"""
Retrieves the part number of the PSU

Returns:
string: Part number of PSU
"""
# For Serial number "US-01234D-54321-25A-0123-A00", the part
# number is "01234D"
psu_serialno = self._get_pmc_register(self.psu_serialno_reg)
if (psu_serialno != 'ERR') and self.get_presence():
if (len(psu_serialno.split('-')) > 1):
psu_partno = psu_serialno.split('-')[1]
else:
psu_partno = 'NA'
else:
psu_partno = 'NA'

return psu_partno

def get_serial(self):
"""
Retrieves the serial number of the PSU

Returns:
string: Serial number of PSU
"""
# Sample Serial number format "US-01234D-54321-25A-0123-A00"
psu_serialno = self._get_pmc_register(self.psu_serialno_reg)
if (psu_serialno == 'ERR') or not self.get_presence():
psu_serialno = 'NA'

return psu_serialno

def get_status(self):
"""
Retrieves the operational status of the PSU

Returns:
bool: True if PSU is operating properly, False if not
"""
status = False
psu_status = self._get_pmc_register(self.psu_presence_reg)
if (psu_status != 'ERR'):
psu_status = int(psu_status, 16)
# Checking whether both bit 3 and bit 2 are not set
if (~psu_status & 0b1000) and (~psu_status & 0b0100):
status = True

return status

def get_voltage(self):
"""
Retrieves current PSU voltage output

Returns:
A float number, the output voltage in volts,
e.g. 12.1
"""
psu_voltage = self._get_pmc_register(self.psu_voltage_reg)
if (psu_voltage != 'ERR') and self.get_presence():
# Converting the value returned by driver which is in
# millivolts to volts
psu_voltage = float(psu_voltage) / 1000
else:
psu_voltage = 0.0

return psu_voltage

def get_current(self):
"""
Retrieves present electric current supplied by PSU

Returns:
A float number, electric current in amperes,
e.g. 15.4
"""
psu_current = self._get_pmc_register(self.psu_current_reg)
if (psu_current != 'ERR') and self.get_presence():
# Converting the value returned by driver which is in
# milliamperes to amperes
psu_current = float(psu_current) / 1000
else:
psu_current = 0.0

return psu_current

def get_power(self):
"""
Retrieves current energy supplied by PSU

Returns:
A float number, the power in watts,
e.g. 302.6
"""
psu_power = self._get_pmc_register(self.psu_power_reg)
if (psu_power != 'ERR') and self.get_presence():
# Converting the value returned by driver which is in
# microwatts to watts
psu_power = float(psu_power) / 1000000
else:
psu_power = 0.0

return psu_power

def get_powergood_status(self):
"""
Retrieves the powergood status of PSU

Returns:
A boolean, True if PSU has stablized its output voltages and
passed all its internal self-tests, False if not.
"""
status = False
if self.get_status() and self._fan_list[0].get_status():
status = True

return status

def get_status_led(self):
"""
Gets the state of the PSU status LED

Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings.
"""
if self.get_powergood_status():
return self.STATUS_LED_COLOR_GREEN
else:
return self.STATUS_LED_COLOR_OFF

def set_status_led(self, color):
"""
Sets the state of the PSU status LED
Args:
color: A string representing the color with which to set the
PSU status LED
Returns:
bool: True if status LED state is set successfully, False if
not
"""
# In Z9100, SmartFusion FPGA controls the PSU LED and the PSU
# LED state cannot be changed from CPU.
return False
Loading