[201911] DellEMC platform API 2.0 for Z9264f, S5232f (#5637)

Add platform API 2.0 support for Z9264f, S5232f in 201911 branch
This commit is contained in:
Aravind Mani 2020-10-28 22:31:47 +05:30 committed by GitHub
parent 5a802533b5
commit 3734bf326b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 5065 additions and 60 deletions

View File

@ -18,5 +18,13 @@ except ImportError, e:
class board(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self, name, path, cpld_root, ro):
self.eeprom_path = "/sys/class/i2c-adapter/i2c-0/0-0050/eeprom"
self.eeprom_path = None
for b in (0,1):
f = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'.format(b)
if os.path.exists(f):
self.eeprom_path = f
break
if self.eeprom_path is None:
return
super(board, self).__init__(self.eeprom_path, 0, '', True)

View File

@ -44,6 +44,8 @@ SFP_VOLT_OFFSET = 98
SFP_VOLT_WIDTH = 2
SFP_MODULE_THRESHOLD_OFFSET = 0
SFP_MODULE_THRESHOLD_WIDTH = 56
SFP_CHANNL_MON_OFFSET = 100
SFP_CHANNL_MON_WIDTH = 6
XCVR_DOM_CAPABILITY_OFFSET = 92
XCVR_DOM_CAPABILITY_WIDTH = 1
@ -226,8 +228,8 @@ class SfpUtil(SfpUtilBase):
if (reg_value == ""):
return False
# Mask off 4th bit for presence
mask = (1 << 6)
# Mask off 4th bit for reset
mask = (1 << 4)
# ResetL is active low
reg_value = reg_value & ~mask
@ -347,7 +349,7 @@ class SfpUtil(SfpUtilBase):
]
transceiver_dom_info_dict = dict.fromkeys(dom_info_dict_keys, 'N/A')
if port_num in self.qsfp_ports:
if port_num in self.qsfp_ports:
offset = 0
offset_xcvr = 128
file_path = self._get_port_eeprom_path(port_num, self.IDENTITY_EEPROM_ADDR)
@ -379,7 +381,7 @@ class SfpUtil(SfpUtilBase):
return transceiver_dom_info_dict
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_TEMPE_OFFSET), QSFP_TEMPE_WIDTH)
if dom_temperature_raw is not None:
if dom_temperature_raw is not None:
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
else:
return transceiver_dom_info_dict
@ -411,10 +413,22 @@ class SfpUtil(SfpUtilBase):
else:
return transceiver_dom_info_dict
transceiver_dom_info_dict['tx1power'] = 'N/A'
transceiver_dom_info_dict['tx1power'] = 'N/A'
transceiver_dom_info_dict['tx2power'] = 'N/A'
transceiver_dom_info_dict['tx3power'] = 'N/A'
transceiver_dom_info_dict['tx4power'] = 'N/A'
else:
dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + QSFP_CHANNL_MON_OFFSET), QSFP_CHANNL_MON_WITH_TX_POWER_WIDTH)
if dom_channel_monitor_raw is not None:
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params_with_tx_power(dom_channel_monitor_raw, 0)
else:
return None
transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TX1Power']['value']
transceiver_dom_info_dict['tx2power'] = dom_channel_monitor_data['data']['TX2Power']['value']
transceiver_dom_info_dict['tx3power'] = dom_channel_monitor_data['data']['TX3Power']['value']
transceiver_dom_info_dict['tx4power'] = dom_channel_monitor_data['data']['TX4Power']['value']
try:
sysfsfile_eeprom.close()
except IOError:
@ -433,64 +447,66 @@ class SfpUtil(SfpUtilBase):
transceiver_dom_info_dict['tx4bias'] = dom_channel_monitor_data['data']['TX4Bias']['value']
else:
offset = 256
file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR)
if not self._sfp_eeprom_present(file_path, 0):
return None
offset = 256
file_path = self._get_port_eeprom_path(port_num, self.DOM_EEPROM_ADDR)
if not self._sfp_eeprom_present(file_path, 0):
return None
try:
sysfsfile_eeprom = io.open(file_path,"rb",0)
except IOError:
print("Error: reading sysfs file %s" % file_path)
return None
sfpd_obj = sff8472Dom(None,1)
if sfpd_obj is None:
return None
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_TEMPE_OFFSET),
SFP_TEMPE_WIDTH)
if dom_temperature_raw is not None:
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
else:
return transceiver_dom_info_dict
try:
sysfsfile_eeprom = io.open(file_path,"rb",0)
except IOError:
print("Error: reading sysfs file %s" % file_path)
return None
dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_VOLT_OFFSET),
SFP_VOLT_WIDTH)
if dom_voltage_raw is not None:
dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0)
else:
return transceiver_dom_info_dict
sfpd_obj = sff8472Dom(None,1)
if sfpd_obj is None:
return None
dom_temperature_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_TEMPE_OFFSET),
SFP_TEMPE_WIDTH)
dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_MODULE_THRESHOLD_OFFSET),
SFP_MODULE_THRESHOLD_WIDTH)
if dom_channel_monitor_raw is not None:
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0)
else:
return transceiver_dom_info_dict
if dom_temperature_raw is not None:
dom_temperature_data = sfpd_obj.parse_temperature(dom_temperature_raw, 0)
else:
return transceiver_dom_info_dict
try:
sysfsfile_eeprom.close()
except IOError:
print("Error: closing sysfs file %s" % file_path)
return None
dom_voltage_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_VOLT_OFFSET),
SFP_VOLT_WIDTH)
transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value']
transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value']
transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value']
transceiver_dom_info_dict['rx2power'] = 'N/A'
transceiver_dom_info_dict['rx3power'] = 'N/A'
transceiver_dom_info_dict['rx4power'] = 'N/A'
transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value']
transceiver_dom_info_dict['tx2bias'] = 'N/A'
transceiver_dom_info_dict['tx3bias'] = 'N/A'
transceiver_dom_info_dict['tx4bias'] = 'N/A'
transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value']
transceiver_dom_info_dict['tx2power'] = 'N/A'
transceiver_dom_info_dict['tx3power'] = 'N/A'
transceiver_dom_info_dict['tx4power'] = 'N/A'
if dom_voltage_raw is not None:
dom_voltage_data = sfpd_obj.parse_voltage(dom_voltage_raw, 0)
else:
return transceiver_dom_info_dict
dom_channel_monitor_raw = self._read_eeprom_specific_bytes(sysfsfile_eeprom, (offset + SFP_CHANNL_MON_OFFSET),
SFP_CHANNL_MON_WIDTH)
if dom_channel_monitor_raw is not None:
dom_channel_monitor_data = sfpd_obj.parse_channel_monitor_params(dom_channel_monitor_raw, 0)
else:
return transceiver_dom_info_dict
try:
sysfsfile_eeprom.close()
except IOError:
print("Error: closing sysfs file %s" % file_path)
return None
transceiver_dom_info_dict['temperature'] = dom_temperature_data['data']['Temperature']['value']
transceiver_dom_info_dict['voltage'] = dom_voltage_data['data']['Vcc']['value']
transceiver_dom_info_dict['rx1power'] = dom_channel_monitor_data['data']['RXPower']['value']
transceiver_dom_info_dict['rx2power'] = 'N/A'
transceiver_dom_info_dict['rx3power'] = 'N/A'
transceiver_dom_info_dict['rx4power'] = 'N/A'
transceiver_dom_info_dict['tx1bias'] = dom_channel_monitor_data['data']['TXBias']['value']
transceiver_dom_info_dict['tx2bias'] = 'N/A'
transceiver_dom_info_dict['tx3bias'] = 'N/A'
transceiver_dom_info_dict['tx4bias'] = 'N/A'
transceiver_dom_info_dict['tx1power'] = dom_channel_monitor_data['data']['TXPower']['value']
transceiver_dom_info_dict['tx2power'] = 'N/A'
transceiver_dom_info_dict['tx3power'] = 'N/A'
transceiver_dom_info_dict['tx4power'] = 'N/A'
return transceiver_dom_info_dict
return transceiver_dom_info_dict
def get_transceiver_dom_threshold_info_dict(self, port_num):
transceiver_dom_threshold_info_dict = {}
dom_info_dict_keys = ['temphighalarm', 'temphighwarning',

View File

@ -1807,6 +1807,16 @@ static ssize_t show_psu(struct device *dev,
}
ret = pow/10;
break;
case 11:
psu_status = smf_read_reg(data, PSU_1_STATUS);
if (psu_status &(2))
ret=1;
break;
case 12:
psu_status = smf_read_reg(data, PSU_2_STATUS);
if (psu_status &(2))
ret=1;
break;
default:
return ret;
}
@ -2055,7 +2065,8 @@ static SENSOR_DEVICE_ATTR(psu2_presence, S_IRUGO, show_psu, NULL, 6);
static SENSOR_DEVICE_ATTR(psu1_serialno, S_IRUGO, show_ppid, NULL, 10);
static SENSOR_DEVICE_ATTR(psu2_serialno, S_IRUGO, show_ppid, NULL, 11);
static SENSOR_DEVICE_ATTR(current_total_power, S_IRUGO, show_psu, NULL, 10);
static SENSOR_DEVICE_ATTR(psu1_type, S_IRUGO, show_psu, NULL, 11);
static SENSOR_DEVICE_ATTR(psu2_type, S_IRUGO, show_psu, NULL, 12);
/* SMF Version */
static SENSOR_DEVICE_ATTR(smf_version, S_IRUGO, show_smf_version, NULL, 0);
static SENSOR_DEVICE_ATTR(smf_firmware_ver, S_IRUGO, show_smf_version, NULL, 1);

View File

@ -0,0 +1,270 @@
#! /usr/bin/python
########################################################################
# DellEMC
#
# Module contains implementation of IpmiSensor and IpmiFru classes that
# provide Sensor's and FRU's information respectively.
#
########################################################################
import subprocess
import re
# IPMI Request Network Function Codes
NetFn_SensorEvent = 0x04
NetFn_Storage = 0x0A
# IPMI Sensor Device Commands
Cmd_GetSensorReadingFactors = 0x23
Cmd_GetSensorThreshold = 0x27
Cmd_GetSensorReading = 0x2D
# IPMI FRU Device Commands
Cmd_ReadFRUData = 0x11
class IpmiSensor(object):
# Sensor Threshold types and their respective bit masks
THRESHOLD_BIT_MASK = {
"LowerNonCritical" : 0,
"LowerCritical" : 1,
"LowerNonRecoverable" : 2,
"UpperNonCritical" : 3,
"UpperCritical" : 4,
"UpperNonRecoverable" : 5
}
def __init__(self, sensor_id, is_discrete=False):
self.id = sensor_id
self.is_discrete = is_discrete
def _get_ipmitool_raw_output(self, args):
"""
Returns a list the elements of which are the individual bytes of
ipmitool raw <cmd> command output.
"""
result_bytes = list()
result = ""
command = "ipmitool raw {}".format(args)
try:
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
if not proc.returncode:
result = stdout.rstrip('\n')
except Exception as e:
pass
for i in result.split():
result_bytes.append(int(i, 16))
return result_bytes
def _get_converted_sensor_reading(self, raw_value):
"""
Returns a 2 element tuple(bool, int) in which first element
provides the validity of the reading and the second element is
the converted sensor reading
"""
# Get Sensor Reading Factors
cmd_args = "{} {} {} {}".format(NetFn_SensorEvent,
Cmd_GetSensorReadingFactors,
self.id, raw_value)
factors = self._get_ipmitool_raw_output(cmd_args)
if len(factors) != 7:
return False, 0
# Compute Twos complement
def get_twos_complement(val, bits):
if val & (1 << (bits - 1)):
val = val - (1 << bits)
return val
# Calculate actual sensor value from the raw sensor value
# using the sensor reading factors.
M = get_twos_complement(((factors[2] & 0xC0) << 8) | factors[1], 10)
B = get_twos_complement(((factors[4] & 0xC0) << 8) | factors[3], 10)
R_exp = get_twos_complement((factors[6] & 0xF0) >> 4, 4)
B_exp = get_twos_complement(factors[6] & 0x0F, 4)
converted_reading = ((M * raw_value) + (B * 10**B_exp)) * 10**R_exp
return True, converted_reading
def get_reading(self):
"""
For Threshold sensors, returns the sensor reading.
For Discrete sensors, returns the state value.
Returns:
A tuple (bool, int) where the first element provides the
validity of the reading and the second element provides the
sensor reading/state value.
"""
# Get Sensor Reading
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorReading,
self.id)
output = self._get_ipmitool_raw_output(cmd_args)
if len(output) != 4:
return False, 0
# Check reading/state unavailable
if output[1] & 0x20:
return False, 0
if self.is_discrete:
state = ((output[3] & 0x7F) << 8) | output[2]
return True, state
else:
return self._get_converted_sensor_reading(output[0])
def get_threshold(self, threshold_type):
"""
Returns the sensor's threshold value for a given threshold type.
Args:
threshold_type (str) - one of the below mentioned
threshold type strings
"LowerNonCritical"
"LowerCritical"
"LowerNonRecoverable"
"UpperNonCritical"
"UpperCritical"
"UpperNonRecoverable"
Returns:
A tuple (bool, int) where the first element provides the
validity of that threshold and second element provides the
threshold value.
"""
# Thresholds are not valid for discrete sensors
if self.is_discrete:
raise TypeError("Threshold is not applicable for Discrete Sensor")
if threshold_type not in self.THRESHOLD_BIT_MASK.keys():
raise ValueError("Invalid threshold type {} provided. Valid types "
"are {}".format(threshold_type,
self.THRESHOLD_BIT_MASK.keys()))
bit_mask = self.THRESHOLD_BIT_MASK[threshold_type]
# Get Sensor Threshold
cmd_args = "{} {} {}".format(NetFn_SensorEvent, Cmd_GetSensorThreshold,
self.id)
thresholds = self._get_ipmitool_raw_output(cmd_args)
if len(thresholds) != 7:
return False, 0
valid_thresholds = thresholds.pop(0)
# Check whether particular threshold is readable
if valid_thresholds & (1 << bit_mask):
return self._get_converted_sensor_reading(thresholds[bit_mask])
else:
return False, 0
class IpmiFru(object):
def __init__(self, fru_id):
self.id = fru_id
def _get_ipmitool_fru_print(self):
result = ""
command = "ipmitool fru print {}".format(self.id)
try:
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
if not proc.returncode:
result = stdout.rstrip('\n')
except Exception as e:
pass
return result
def _get_from_fru(self, info):
"""
Returns a string containing the info from FRU
"""
fru_output = self._get_ipmitool_fru_print()
if not fru_output:
return "NA"
info_req = re.search(r"%s\s*:(.*)"%info, fru_output)
if not info_req:
return "NA"
return info_req.group(1).strip()
def get_board_serial(self):
"""
Returns a string containing the Serial Number of the device.
"""
return self._get_from_fru('Board Serial')
def get_board_part_number(self):
"""
Returns a string containing the Part Number of the device.
"""
return self._get_from_fru('Board Part Number')
def get_board_mfr_id(self):
"""
Returns a string containing the manufacturer id of the FRU.
"""
return self._get_from_fru('Board Mfg')
def get_board_product(self):
"""
Returns a string containing the manufacturer id of the FRU.
"""
return self._get_from_fru('Board Product')
def get_fru_data(self, offset, count=1):
"""
Reads and returns the FRU data at the provided offset.
Args:
offset (int) - FRU offset to read
count (int) - Number of bytes to read [optional, default = 1]
Returns:
A tuple (bool, list(int)) where the first element provides
the validity of the data read and the second element is a
list, the elements of which are the individual bytes of the
FRU data read.
"""
result_bytes = list()
is_valid = True
result = ""
offset_LSB = offset & 0xFF
offset_MSB = offset & 0xFF00
command = "ipmitool raw {} {} {} {} {} {}".format(NetFn_Storage,
Cmd_ReadFRUData,
self.id, offset_LSB,
offset_MSB, count)
try:
proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
if not proc.returncode:
result = stdout.rstrip('\n')
except Exception as e:
is_valid = False
if (not result) or (not is_valid):
return False, result_bytes
for i in result.split():
result_bytes.append(int(i, 16))
read_count = result_bytes.pop(0)
if read_count != count:
return False, result_bytes
else:
return True, result_bytes

View File

@ -0,0 +1,24 @@
# Helper functions to access hardware
import struct
import mmap
import subprocess
# Read PCI device
def pci_mem_read(mm, offset):
mm.seek(offset)
read_data_stream = mm.read(4)
return struct.unpack('I',read_data_stream)[0]
def pci_get_value(resource, offset):
with open(resource, 'r+b') as fd:
mm = mmap.mmap(fd.fileno(), 0)
val = pci_mem_read(mm, offset)
mm.close()
return val
# Read I2C device
def i2c_get(bus, i2caddr, ofs):
return int(subprocess.check_output(['/usr/sbin/i2cget', '-y', str(bus), str(i2caddr), str(ofs)]), 16)

View File

@ -6,5 +6,6 @@ s5232f/scripts/pcisysfs.py usr/bin
s5232f/scripts/qsfp_irq_enable.py usr/bin
s5232f/cfg/s5232f-modules.conf etc/modules-load.d
s5232f/systemd/platform-modules-s5232f.service etc/systemd/system
s5232f/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-dellemc_s5232f_c3538-r0
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_s5232f_c3538-r0
common/fw-updater usr/local/bin

View File

@ -6,5 +6,6 @@ z9264f/scripts/pcisysfs.py usr/bin
z9264f/scripts/qsfp_irq_enable.py usr/bin
z9264f/cfg/z9264f-modules.conf etc/modules-load.d
z9264f/systemd/platform-modules-z9264f.service etc/systemd/system
z9264f/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/x86_64-dellemc_z9264f_c3538-r0
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_z9264f_c3538-r0
common/fw-updater usr/local/bin

View File

@ -29,6 +29,16 @@ override_dh_auto_build:
cd $(MOD_SRC_DIR)/$${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
elif [ $$mod = "z9264f" ]; then \
cp $(COMMON_DIR)/ipmihelper.py $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
cd $(MOD_SRC_DIR)/$${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
elif [ $$mod = "s5232f" ]; then \
cp $(COMMON_DIR)/ipmihelper.py $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
cd $(MOD_SRC_DIR)/$${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
fi; \
echo "making man page alias $$mod -> $$mod APIs";\
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
@ -67,6 +77,16 @@ override_dh_clean:
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
elif [ $$mod = "z9264f" ]; then \
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
elif [ $$mod = "s5232f" ]; then \
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build; \
rm -rf $(MOD_SRC_DIR)/$${mod}/build/*.egg-info; \
fi; \
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules clean; \
done)

View File

@ -133,6 +133,20 @@ platform_firmware_versions() {
r_maj=`/usr/sbin/i2cget -y 600 0x33 0x1 | sed ' s/.*\(0x..\)$/\1/'`
echo "Slave CPLD 4: $((r_maj)).$((r_min))" >> $FIRMWARE_VERSION_FILE
}
install_python_api_package() {
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
}
remove_python_api_package() {
rv=$(pip show sonic-platform > /dev/null 2>/dev/null)
if [ $? -eq 0 ]; then
rv=$(pip uninstall -y sonic-platform > /dev/null 2>/dev/null)
fi
}
init_devnum
if [ "$1" == "init" ]; then
@ -147,6 +161,7 @@ if [ "$1" == "init" ]; then
switch_board_qsfp "new_device"
switch_board_modsel
switch_board_led_default
install_python_api_package
python /usr/bin/qsfp_irq_enable.py
platform_firmware_versions
@ -155,9 +170,9 @@ elif [ "$1" == "deinit" ]; then
switch_board_qsfp "delete_device"
switch_board_sfp "delete_device"
switch_board_qsfp_mux "delete_device"
modprobe -r i2c-mux-pca954x
modprobe -r i2c-dev
remove_python_api_package
else
echo "s5232f_platform : Invalid option !"
fi

View File

@ -0,0 +1 @@
../s6100/setup.py

View File

@ -0,0 +1,2 @@
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "watchdog"]
from sonic_platform import *

View File

@ -0,0 +1,232 @@
#!/usr/bin/env python
#############################################################################
# DELLEMC S5232F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
from __future__ import division
try:
import sys
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.sfp import Sfp
from sonic_platform.eeprom import Eeprom
from sonic_platform.component import Component
from sonic_platform.psu import Psu
from sonic_platform.thermal import Thermal
from sonic_platform.watchdog import Watchdog
from sonic_platform.fan import Fan
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
MAX_S5232F_COMPONENT = 6 # BIOS,BMC,FPGA,SYSTEM CPLD,4 SLAVE CPLDs
MAX_S5232F_FANTRAY =4
MAX_S5232F_FAN = 2
MAX_S5232F_PSU = 2
MAX_S5232F_THERMAL = 8
class Chassis(ChassisBase):
"""
DELLEMC Platform-specific Chassis class
"""
oir_fd = -1
epoll = -1
_global_port_pres_dict = {}
def __init__(self):
ChassisBase.__init__(self)
# sfp.py will read eeprom contents and retrive the eeprom data.
# We pass the eeprom path from chassis.py
self.PORT_START = 1
self.PORT_END = 34
self.PORTS_IN_BLOCK = (self.PORT_END + 1)
_sfp_port = range(33, self.PORT_END + 1)
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
for index in range(self.PORT_START, self.PORTS_IN_BLOCK):
port_num = index + 1
eeprom_path = eeprom_base.format(port_num)
if index not in _sfp_port:
sfp_node = Sfp(index, 'QSFP', eeprom_path)
else:
sfp_node = Sfp(index, 'SFP', eeprom_path)
self._sfp_list.append(sfp_node)
self._eeprom = Eeprom()
self._watchdog = Watchdog()
self._num_sfps = self.PORT_END
self._num_fans = MAX_S5232F_FANTRAY * MAX_S5232F_FAN
self._fan_list = [Fan(i, j) for i in range(MAX_S5232F_FANTRAY) \
for j in range(MAX_S5232F_FAN)]
self._psu_list = [Psu(i) for i in range(MAX_S5232F_PSU)]
self._thermal_list = [Thermal(i) for i in range(MAX_S5232F_THERMAL)]
self._component_list = [Component(i) for i in range(MAX_S5232F_COMPONENT)]
for port_num in range(self.PORT_START, self.PORTS_IN_BLOCK):
# sfp get uses zero-indexing, but port numbers start from 1
presence = self.get_sfp(port_num).get_presence()
self._global_port_pres_dict[port_num] = '1' if presence else '0'
def __del__(self):
if self.oir_fd != -1:
self.epoll.unregister(self.oir_fd.fileno())
self.epoll.close()
self.oir_fd.close()
# check for this event change for sfp / do we need to handle timeout/sleep
def get_change_event(self, timeout=0):
from time import sleep
"""
Returns a nested dictionary containing all devices which have
experienced a change at chassis level
"""
port_dict = {}
change_dict = {}
change_dict['sfp'] = port_dict
elapsed_time_ms = 0
sleep_time_ms = 500
sleep_time = sleep_time_ms / 1000
while True:
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
presence = self.get_sfp(port_num).get_presence()
if(presence and self._global_port_pres_dict[port_num] == '0'):
self._global_port_pres_dict[port_num] = '1'
port_dict[port_num] = '1'
elif(not presence and
self._global_port_pres_dict[port_num] == '1'):
self._global_port_pres_dict[port_num] = '0'
port_dict[port_num] = '0'
if(len(port_dict) > 0):
return True, change_dict
if timeout != 0:
elapsed_time_ms += sleep_time_ms
if elapsed_time_ms > timeout:
break
sleep(sleep_time)
return True, change_dict
def get_sfp(self, index):
"""
Retrieves sfp represented by (0-based) index <index>
Args:
index: An integer, the index (0-based) of the sfp to retrieve.
The index should be the sequence of a physical port in a chassis,
starting from 0.
For example, 0 for Ethernet0, 1 for Ethernet4 and so on.
Returns:
An object dervied from SfpBase representing the specified sfp
"""
sfp = None
try:
# The index will start from 0
sfp = self._sfp_list[index-1]
except IndexError:
sys.stderr.write("SFP index {} out of range (0-{})\n".format(
index, len(self._sfp_list)-1))
return sfp
def get_name(self):
"""
Retrieves the name of the chassis
Returns:
string: The name of the chassis
"""
return self._eeprom.modelstr()
def get_presence(self):
"""
Retrieves the presence of the chassis
Returns:
bool: True if chassis is present, False if not
"""
return True
def get_model(self):
"""
Retrieves the model number (or part number) of the chassis
Returns:
string: Model/part number of chassis
"""
return self._eeprom.part_number_str()
def get_serial(self):
"""
Retrieves the serial number of the chassis (Service tag)
Returns:
string: Serial number of chassis
"""
return self._eeprom.serial_str()
def get_status(self):
"""
Retrieves the operational status of the chassis
Returns:
bool: A boolean value, True if chassis is operating properly
False if not
"""
return True
def get_base_mac(self):
"""
Retrieves the base MAC address for the chassis
Returns:
A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX'
"""
return self._eeprom.base_mac_addr('')
def get_serial_number(self):
"""
Retrieves the hardware serial number for the chassis
Returns:
A string containing the hardware serial number for this chassis.
"""
return self._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._eeprom.system_eeprom_info()
def get_eeprom(self):
"""
Retrieves the Sys Eeprom instance for the chassis.
Returns :
The instance of the Sys Eeprom
"""
return self._eeprom
def get_num_fans(self):
"""
Retrives the number of Fans on the chassis.
Returns :
An integer represents the number of Fans on the chassis.
"""
return self._num_fans
def get_num_sfps(self):
"""
Retrives the numnber of Media on the chassis.
Returns:
An integer represences the number of SFPs on the chassis.
"""
return self._num_sfps

View File

@ -0,0 +1,122 @@
#!/usr/bin/env python
########################################################################
# DELLEMC S5232F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Components' (e.g., BIOS, CPLD, FPGA, BMC etc.) available in
# the platform
#
########################################################################
try:
import subprocess
from sonic_platform_base.component_base import ComponentBase
import sonic_platform.hwaccess as hwaccess
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
def get_bios_version():
return subprocess.check_output(['dmidecode', '-s', 'system-version']).strip()
def get_fpga_version():
val = hwaccess.pci_get_value('/sys/bus/pci/devices/0000:04:00.0/resource0', 0)
return '{}.{}'.format((val >> 8) & 0xff, val & 0xff)
def get_bmc_version():
return subprocess.check_output(
['cat', '/sys/class/ipmi/ipmi0/device/bmc/firmware_revision']
).strip()
def get_cpld_version(bus, i2caddr):
return '{}.{}'.format(hwaccess.i2c_get(bus, i2caddr, 1),
hwaccess.i2c_get(bus, i2caddr, 0)
)
def get_cpld0_version():
return get_cpld_version(601, 0x31)
def get_cpld1_version():
return get_cpld_version(600, 0x30)
def get_cpld2_version():
return get_cpld_version(600, 0x31)
class Component(ComponentBase):
"""DellEMC Platform-specific Component class"""
CHASSIS_COMPONENTS = [
['BIOS',
'Performs initialization of hardware components during booting',
get_bios_version
],
['FPGA',
'Used for managing the system LEDs',
get_fpga_version
],
['BMC',
'Platform management controller for on-board temperature,monitoring, in-chassis power, Fan and LED control',
get_bmc_version
],
['System CPLD',
'Used for managing the CPU power sequence and CPU states',
get_cpld0_version
],
['Slave CPLD 1',
'Used for managing SFP28/QSFP28 port transceivers (SFP28 1-24, QSFP28 1-4)',
get_cpld1_version
],
['Slave CPLD 2',
'Used for managing SFP28/QSFP28 port transceivers (SFP28 25-48, QSFP28 5-8)',
get_cpld2_version
],
]
def __init__(self, component_index = 0):
self.index = component_index
self.name = self.CHASSIS_COMPONENTS[self.index][0]
self.description = self.CHASSIS_COMPONENTS[self.index][1]
#self.version = self.CHASSIS_COMPONENTS[self.index][2]()
def get_name(self):
"""
Retrieves the name of the component
Returns:
A string containing the name of the component
"""
return self.name
def get_description(self):
"""
Retrieves the description of the component
Returns:
A string containing the description of the component
"""
return self.description
def get_firmware_version(self):
"""
Retrieves the firmware version of the component
Returns:
A string containing the firmware version of the component
"""
return self.version
def install_firmware(self, image_path):
"""
Installs firmware to the component
Args:
image_path: A string, path to firmware image
Returns:
A boolean, True if install was successful, False if not
"""
return False

View File

@ -0,0 +1,141 @@
#!/usr/bin/env python
#############################################################################
# DellEmc S5248F
#
# Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings:
# - the eeprom format definition
# - specific encoder/decoder if there is special need
#############################################################################
try:
import os.path
from sonic_eeprom import eeprom_tlvinfo
import binascii
except ImportError, e:
raise ImportError(str(e) + "- required module not found")
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self):
self.eeprom_path = None
for b in (0, 1):
f = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'.format(b)
if os.path.exists(f):
self.eeprom_path = f
break
if self.eeprom_path is None:
return
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")
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):
"""
Returns the serial number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_SERIAL_NUMBER)
if not is_valid:
return "N/A"
return results[2]
def base_mac_addr(self, e):
"""
Returns the base mac address found in the system EEPROM
"""
(is_valid, t) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_MAC_BASE)
if not is_valid or t[1] != 6:
return super(eeprom_tlvinfo.TlvInfoDecoder, self).switchaddrstr(t)
return ":".join([binascii.b2a_hex(T) for T in t[2]])
def modelstr(self):
"""
Returns the Model name
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_PRODUCT_NAME)
if not is_valid:
return "N/A"
return results[2]
def part_number_str(self):
"""
Returns the part number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_PART_NUMBER)
if not is_valid:
return "N/A"
return results[2]
def serial_str(self):
"""
Returns the servicetag number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_SERVICE_TAG)
if not is_valid:
return "N/A"
return results[2]
def revision_str(self):
"""
Returns the device revision
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_DEVICE_VERSION)
if not is_valid:
return "N/A"
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

View File

@ -0,0 +1,182 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5232F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fans' information which are available in the platform.
#
########################################################################
try:
from sonic_platform_base.fan_base import FanBase
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
FAN1_MAX_SPEED_OFFSET = 71
FAN2_MAX_SPEED_OFFSET = 73
PSU_FAN_MAX_SPEED_OFFSET = 50
FAN_DIRECTION_OFFSET = 69
PSU_FAN_DIRECTION_OFFSET = 47
class Fan(FanBase):
"""DellEMC Platform-specific Fan class"""
# { FAN-ID: { Sensor-Name: Sensor-ID } }
FAN_SENSOR_MAPPING = { 1: {"Prsnt": 0x51, "State": 0x64, "Speed": 0x24},
2: {"Prsnt": 0x51, "State": 0x60, "Speed": 0x20},
3: {"Prsnt": 0x52, "State": 0x65, "Speed": 0x25},
4: {"Prsnt": 0x52, "State": 0x61, "Speed": 0x21},
5: {"Prsnt": 0x53, "State": 0x66, "Speed": 0x26},
6: {"Prsnt": 0x53, "State": 0x62, "Speed": 0x22},
7: {"Prsnt": 0x54, "State": 0x67, "Speed": 0x27},
8: {"Prsnt": 0x54, "State": 0x63, "Speed": 0x23} }
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x46, "Speed": 0x2e},
2: {"State": 0x47, "Speed": 0x2f} }
# { FANTRAY-ID: FRU-ID }
FAN_FRU_MAPPING = { 1: 3, 2: 4, 3: 5, 4: 6 }
PSU_FRU_MAPPING = { 1: 1, 2: 2 }
def __init__(self, fantray_index=1, fan_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
if (self.fanindex == 1):
self.max_speed_offset = FAN1_MAX_SPEED_OFFSET
else:
self.max_speed_offset = FAN2_MAX_SPEED_OFFSET
self.fan_direction_offset = FAN_DIRECTION_OFFSET
self.index = (self.fantrayindex - 1) * 2 + self.fanindex
self.prsnt_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Prsnt"],
is_discrete=True)
self.state_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["State"],
is_discrete=True)
self.speed_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Speed"])
self.fru = IpmiFru(self.FAN_FRU_MAPPING[self.fantrayindex])
else:
self.dependency = dependency
self.fanindex = fan_index
self.state_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["State"],
is_discrete=True)
self.speed_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["Speed"])
self.fru = IpmiFru(self.PSU_FRU_MAPPING[self.fanindex])
self.max_speed_offset = PSU_FAN_MAX_SPEED_OFFSET
self.fan_direction_offset = PSU_FAN_DIRECTION_OFFSET
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
def get_name(self):
"""
Retrieves the name of the device
Returns:
String: The name of the device
"""
if self.is_psu_fan:
return "PSU{} Fan".format(self.fanindex)
else:
return "FanTray{}-Fan{}".format(self.fantrayindex, self.fanindex)
def get_model(self):
"""
Retrieves the part number of the FAN
Returns:
String: Part number of FAN
"""
if self.is_psu_fan:
return 'NA'
else:
return self.fru.get_board_part_number()
def get_serial(self):
"""
Retrieves the serial number of the FAN
Returns:
String: Serial number of FAN
"""
if self.is_psu_fan:
return 'NA'
else:
return self.fru.get_board_serial()
def get_presence(self):
"""
Retrieves the presence of the FAN
Returns:
bool: True if fan is present, False if not
"""
presence = False
if self.is_psu_fan:
return self.dependency.get_presence()
else:
is_valid, state = self.prsnt_sensor.get_reading()
if is_valid:
if (state & 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
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state == 0x00):
status = True
return status
def get_direction(self):
"""
Retrieves the fan airfow 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_EXHAUST, self.FAN_DIRECTION_INTAKE]
fan_status = self.get_status()
if not fan_status:
return 'NA'
is_valid, fan_direction = self.fru.get_fru_data(self.fan_direction_offset)
if is_valid:
return direction[fan_direction[0]]
else:
return 'NA'
def get_speed(self):
"""
Retrieves the speed of the fan
Returns:
int: percentage of the max fan speed
"""
if self.max_speed == 0:
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
is_valid, fan_speed = self.speed_sensor.get_reading()
if not is_valid or self.max_speed == 0:
speed = 0
else:
speed = (100 * fan_speed)/self.max_speed
return speed
def get_speed_rpm(self):
"""
Retrieves the speed of the fan
Returns:
int: percentage of the max fan speed
"""
fan_speed = 0
is_valid, fan_speed = self.speed_sensor.get_reading()
return fan_speed

View File

@ -0,0 +1 @@
../../common/sonic_platform/hwaccess.py

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
#############################################################################
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
from sonic_platform_base.platform_base import PlatformBase
from sonic_platform.chassis import Chassis
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Platform(PlatformBase):
"""
DELLEMC Platform-specific class
"""
def __init__(self):
PlatformBase.__init__(self)
self._chassis = Chassis()

View File

@ -0,0 +1,179 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5232F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the PSUs' information which are available in the platform
#
########################################################################
try:
from sonic_platform_base.psu_base import PsuBase
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
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"""
# { PSU-ID: { Sensor-Name: Sensor-ID } }
SENSOR_MAPPING = { 1: { "State": 0x31, "Current": 0x39,
"Power": 0x37, "Voltage": 0x38 },
2: { "State": 0x32, "Current": 0x3F,
"Power": 0x3D, "Voltage": 0x3E } }
# ( PSU-ID: FRU-ID }
FRU_MAPPING = { 1: 1, 2: 2 }
def __init__(self, psu_index):
PsuBase.__init__(self)
# PSU is 1-based in DellEMC platforms
self.index = psu_index + 1
self.state_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["State"],
is_discrete=True)
self.voltage_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Voltage"])
self.current_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Current"])
self.power_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Power"])
self.fru = IpmiFru(self.FRU_MAPPING[self.index])
self._fan_list.append(Fan(fantray_index=1,fan_index=self.index, psu_fan=True,
dependency=self))
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
"""
presence = False
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state & 0b1):
presence = True
return presence
def get_model(self):
"""
Retrieves the part number of the PSU
Returns:
string: Part number of PSU
"""
return self.fru.get_board_part_number()
def get_serial(self):
"""
Retrieves the serial number of the PSU
Returns:
string: Serial number of PSU
"""
return self.fru.get_board_serial()
def get_status(self):
"""
Retrieves the operational status of the PSU
Returns:
bool: True if PSU is operating properly, False if not
"""
status = False
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state == 0x01):
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
"""
is_valid, voltage = self.voltage_sensor.get_reading()
if not is_valid:
voltage = 0
return float(voltage)
def get_current(self):
"""
Retrieves present electric current supplied by PSU
Returns:
A float number, electric current in amperes,
e.g. 15.4
"""
is_valid, current = self.current_sensor.get_reading()
if not is_valid:
current = 0
return float(current)
def get_power(self):
"""
Retrieves current energy supplied by PSU
Returns:
A float number, the power in watts,
e.g. 302.6
"""
is_valid, power = self.power_sensor.get_reading()
if not is_valid:
power = 0
return float(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
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state == 0x01):
status = True
return status
def get_mfr_id(self):
"""
Retrives the Manufacturer Id of PSU
Returns:
A string, the manunfacturer id.
"""
return self.fru.get_board_mfr_id()
def get_type(self):
"""
Retrives the Power Type of PSU
Returns :
A string, PSU power type
"""
info = self.fru.get_board_product().split(',')
if 'AC' in info : return 'AC'
if 'DC' in info : return 'DC'
return 'Unknown'

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5232F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Thermals' information which are available in the platform
#
########################################################################
try:
from sonic_platform_base.thermal_base import ThermalBase
from sonic_platform.ipmihelper import IpmiSensor
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Thermal(ThermalBase):
"""DellEMC Platform-specific Thermal class"""
# [ Sensor-Name, Sensor-ID ]
SENSOR_MAPPING = [
['CPU On-board', 0xe],
['ASIC On-board', 0x2],
['System Front Left', 0x3],
['System Front Middle', 0x1],
['System Front Right', 0x4],
['Inlet Airflow Sensor', 0x5],
['PSU1 Airflow Sensor', 0x7],
['PSU2 Airflow Sensor', 0x8]
]
def __init__(self, thermal_index=0):
ThermalBase.__init__(self)
self.index = thermal_index + 1
self.sensor = IpmiSensor(self.SENSOR_MAPPING[self.index - 1][1])
def get_name(self):
"""
Retrieves the name of the thermal
Returns:
string: The name of the thermal
"""
return self.SENSOR_MAPPING[self.index - 1][0]
def get_presence(self):
"""
Retrieves the presence of the thermal
Returns:
bool: True if thermal is present, False if not
"""
return True
def get_model(self):
"""
Retrieves the model number (or part number) of the Thermal
Returns:
string: Model/part number of Thermal
"""
return 'NA'
def get_serial(self):
"""
Retrieves the serial number of the Thermal
Returns:
string: Serial number of Thermal
"""
return 'NA'
def get_status(self):
"""
Retrieves the operational status of the thermal
Returns:
A boolean value, True if thermal is operating properly,
False if not
"""
return True
def get_temperature(self):
"""
Retrieves current temperature reading from thermal
Returns:
A float number of current temperature in Celsius up to
nearest thousandth of one degree Celsius, e.g. 30.125
"""
is_valid, temperature = self.sensor.get_reading()
if not is_valid:
temperature = 0
return float(temperature)
def get_high_threshold(self):
"""
Retrieves the high threshold temperature of thermal
Returns:
A float number, the high threshold temperature of thermal in
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
is_valid, high_threshold = self.sensor.get_threshold("UpperNonRecoverable")
if not is_valid:
high_threshold = 0
return float(high_threshold)
def get_low_threshold(self):
"""
Retrieves the low threshold temperature of thermal
Returns:
A float number, the low threshold temperature of thermal in
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
is_valid, low_threshold = self.sensor.get_threshold("LowerNonRecoverable")
if not is_valid:
low_threshold = 0
return float(low_threshold)
def set_high_threshold(self, temperature):
"""
Sets the high threshold temperature of thermal
Args :
temperature: A float number up to nearest thousandth of one
degree Celsius, e.g. 30.125
Returns:
A boolean, True if threshold is set successfully, False if
not
"""
# Thermal threshold values are pre-defined based on HW.
return False
def set_low_threshold(self, temperature):
"""
Sets the low threshold temperature of thermal
Args :
temperature: A float number up to nearest thousandth of one
degree Celsius, e.g. 30.125
Returns:
A boolean, True if threshold is set successfully, False if
not
"""
# Thermal threshold values are pre-defined based on HW.
return False

View File

@ -0,0 +1,210 @@
#!/usr/bin/env python
########################################################################
#
# DELLEMC S5232f
#
# Abstract base class for implementing a platform-specific class with
# which to interact with a hardware watchdog module in SONiC
#
########################################################################
try:
import ctypes
import subprocess
from sonic_platform_base.watchdog_base import WatchdogBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class _timespec(ctypes.Structure):
_fields_ = [
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
]
class Watchdog(WatchdogBase):
"""
Abstract base class for interfacing with a hardware watchdog module
"""
TIMERS = [15,20,30,40,50,60,65,70]
armed_time = 0
timeout = 0
CLOCK_MONOTONIC = 1
def __init__(self):
self._librt = ctypes.CDLL('librt.so.1', use_errno=True)
self._clock_gettime = self._librt.clock_gettime
self._clock_gettime.argtypes=[ctypes.c_int, ctypes.POINTER(_timespec)]
def _get_command_result(self, cmdline):
try:
proc = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
result = stdout.rstrip('\n')
except OSError:
result = None
return result
def _get_reg_val(self):
# 0x31 = CPLD I2C Base Address
# 0x07 = Watchdog Function Register
value = self._get_command_result("/usr/sbin/i2cget -y 601 0x31 0x07")
if not value:
return None
else:
return int(value, 16)
def _set_reg_val(self,val):
# 0x31 = CPLD I2C Base Address
# 0x07 = Watchdog Function Register
value = self._get_command_result("/usr/sbin/i2cset -y 601 0x31 0x07 %s"
% (val))
return value
def _get_time(self):
"""
To get clock monotonic time
"""
ts = _timespec()
if self._clock_gettime(self.CLOCK_MONOTONIC, ctypes.pointer(ts)) != 0:
self._errno = ctypes.get_errno()
return 0
return ts.tv_sec + ts.tv_nsec * 1e-9
def arm(self, seconds):
"""
Arm the hardware watchdog with a timeout of <seconds> seconds.
If the watchdog is currently armed, calling this function will
simply reset the timer to the provided value. If the underlying
hardware does not support the value provided in <seconds>, this
method should arm the watchdog with the *next greater*
available value.
Returns:
An integer specifying the *actual* number of seconds the
watchdog was armed with. On failure returns -1.
"""
timer_offset = -1
for key,timer_seconds in enumerate(self.TIMERS):
if seconds <= timer_seconds:
timer_offset = key
seconds = timer_seconds
break
if timer_offset == -1:
return -1
# Extracting 5th to 7th bits for WD timer values
# 000 - 15 sec
# 001 - 20 sec
# 010 - 30 sec
# 011 - 40 sec
# 100 - 50 sec
# 101 - 60 sec
# 110 - 65 sec
# 111 - 70 sec
reg_val = self._get_reg_val()
wd_timer_offset = (reg_val >> 4) & 0x7
if wd_timer_offset != timer_offset:
# Setting 5th to 7th bits
# value from timer_offset
self.disarm()
self._set_reg_val(reg_val | (timer_offset << 4))
if self.is_armed():
# Setting last bit to WD Timer punch
# Last bit = WD Timer punch
self._set_reg_val(reg_val & 0xFE)
self.armed_time = self._get_time()
self.timeout = seconds
return seconds
else:
# Setting 4th bit to enable WD
# 4th bit = Enable WD
reg_val = self._get_reg_val()
self._set_reg_val(reg_val | 0x8)
self.armed_time = self._get_time()
self.timeout = seconds
return seconds
def disarm(self):
"""
Disarm the hardware watchdog
Returns:
A boolean, True if watchdog is disarmed successfully, False
if not
"""
if self.is_armed():
# Setting 4th bit to disable WD
# 4th bit = Disable WD
reg_val = self._get_reg_val()
self._set_reg_val(reg_val & 0xF7)
self.armed_time = 0
self.timeout = 0
return True
return False
def is_armed(self):
"""
Retrieves the armed state of the hardware watchdog.
Returns:
A boolean, True if watchdog is armed, False if not
"""
# Extracting 4th bit to get WD Enable/Disable status
# 0 - Disabled WD
# 1 - Enabled WD
reg_val = self._get_reg_val()
wd_offset = (reg_val >> 3) & 1
return bool(wd_offset)
def get_remaining_time(self):
"""
If the watchdog is armed, retrieve the number of seconds
remaining on the watchdog timer
Returns:
An integer specifying the number of seconds remaining on
their watchdog timer. If the watchdog is not armed, returns
-1.
S5232 doesnot have hardware support to show remaining time.
Due to this limitation, this API is implemented in software.
This API would return correct software time difference if it
is called from the process which armed the watchdog timer.
If this API called from any other process, it would return
0. If the watchdog is not armed, this API would return -1.
"""
if not self.is_armed():
return -1
if self.armed_time > 0 and self.timeout != 0:
cur_time = self._get_time()
if cur_time <= 0:
return 0
diff_time = int(cur_time - self.armed_time)
if diff_time > self.timeout:
return self.timeout
else:
return self.timeout - diff_time
return 0

View File

@ -125,6 +125,33 @@ init_switch_port_led() {
fi
}
install_python_api_package() {
device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
}
remove_python_api_package() {
rv=$(pip show sonic-platform > /dev/null 2>/dev/null)
if [ $? -eq 0 ]; then
rv=$(pip uninstall -y sonic-platform > /dev/null 2>/dev/null)
fi
}
get_reboot_cause() {
REBOOT_REASON_FILE="/host/reboot-cause/platform/reboot_reason"
resource="/sys/bus/pci/devices/0000:04:00.0/resource0"
# Handle First Boot into software version with reboot cause determination support
if [[ ! -e $REBOOT_REASON_FILE ]]; then
echo "0" > $REBOOT_REASON_FILE
else
/usr/bin/pcisysfs.py --get --offset 0x18 --res $resource | sed '1d; s/.*:\(.*\)$/\1/;' > $REBOOT_REASON_FILE
fi
/usr/bin/pcisysfs.py --set --val 0x0 --offset 0x18 --res $resource
}
init_devnum
if [ "$1" == "init" ]; then
@ -135,11 +162,13 @@ if [ "$1" == "init" ]; then
modprobe i2c_ocores
modprobe dell_z9264f_fpga_ocores
sys_eeprom "new_device"
get_reboot_cause
switch_board_qsfp_mux "new_device"
switch_board_qsfp "new_device"
switch_board_sfp "new_device"
switch_board_modsel
init_switch_port_led
install_python_api_package
python /usr/bin/qsfp_irq_enable.py
elif [ "$1" == "deinit" ]; then
@ -147,6 +176,7 @@ elif [ "$1" == "deinit" ]; then
switch_board_qsfp "delete_device"
switch_board_qsfp_mux "delete_device"
switch_board_sfp "delete_device"
remove_python_api_package
modprobe -r i2c-mux-pca954x
modprobe -r i2c-dev
else

View File

@ -0,0 +1 @@
../s6100/setup.py

View File

@ -0,0 +1,2 @@
__all__ = ["platform", "chassis", "sfp", "eeprom", "component", "psu", "thermal", "fan", "watchdog"]
from sonic_platform import *

View File

@ -0,0 +1,315 @@
#!/usr/bin/env python
#############################################################################
# DELLEMC Z9264F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
import os
import sys
import select
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.sfp import Sfp
from sonic_platform.eeprom import Eeprom
from sonic_platform.component import Component
from sonic_platform.psu import Psu
from sonic_platform.watchdog import Watchdog
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_Z9264F_FANTRAY =4
MAX_Z9264F_FAN = 2
MAX_Z9264F_COMPONENT = 8 # BIOS,BMC,FPGA,SYSTEM CPLD,4 SLAVE CPLDs
MAX_Z9264F_PSU = 2
MAX_Z9264F_THERMAL = 8
class Chassis(ChassisBase):
"""
DELLEMC Platform-specific Chassis class
"""
REBOOT_CAUSE_PATH = "/host/reboot-cause/platform/reboot_reason"
OIR_FD_PATH = "/sys/bus/pci/devices/0000:04:00.0/port_msi"
oir_fd = -1
epoll = -1
_global_port_pres_dict = {}
def __init__(self):
ChassisBase.__init__(self)
# sfp.py will read eeprom contents and retrive the eeprom data.
# We pass the eeprom path from chassis.py
self.PORT_START = 1
self.PORT_END = 66
PORTS_IN_BLOCK = (self.PORT_END + 1)
_sfp_port = range(65, self.PORT_END + 1)
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
for index in range(self.PORT_START, PORTS_IN_BLOCK):
port_num = index + 1
eeprom_path = eeprom_base.format(port_num)
if index not in _sfp_port:
sfp_node = Sfp(index, 'QSFP', eeprom_path)
else:
sfp_node = Sfp(index, 'SFP', eeprom_path)
self._sfp_list.append(sfp_node)
self._eeprom = Eeprom()
self._watchdog = Watchdog()
for i in range(MAX_Z9264F_COMPONENT):
component = Component(i)
self._component_list.append(component)
for i in range(MAX_Z9264F_PSU):
psu = Psu(i)
self._psu_list.append(psu)
for i in range(MAX_Z9264F_FANTRAY):
for j in range(MAX_Z9264F_FAN):
fan = Fan(i,j)
self._fan_list.append(fan)
for i in range(MAX_Z9264F_THERMAL):
thermal = Thermal(i)
self._thermal_list.append(thermal)
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
presence = self.get_sfp(port_num).get_presence()
if presence:
self._global_port_pres_dict[port_num] = '1'
else:
self._global_port_pres_dict[port_num] = '0'
def __del__(self):
if self.oir_fd != -1:
self.epoll.unregister(self.oir_fd.fileno())
self.epoll.close()
self.oir_fd.close()
def _get_register(self, reg_file):
retval = 'ERR'
if (not os.path.isfile(reg_file)):
print reg_file, 'not found !'
return retval
try:
with os.fdopen(os.open(reg_file, os.O_RDONLY)) as fd:
retval = fd.read()
except Exception as e:
pass
retval = retval.rstrip('\r\n')
retval = retval.lstrip(" ")
return retval
def _check_interrupts(self, port_dict):
retval = 0
is_port_dict_updated = False
for port_num in range(self.PORT_START, (self.PORT_END + 1)):
sfp = self.get_sfp(port_num)
presence = sfp.get_presence()
if(presence and (self._global_port_pres_dict[port_num] == '0')):
is_port_dict_updated = True
self._global_port_pres_dict[port_num] = '1'
port_dict[port_num] = '1'
elif(not presence and (self._global_port_pres_dict[port_num] == '1')):
is_port_dict_updated = True
self._global_port_pres_dict[port_num] = '0'
port_dict[port_num] = '0'
return retval, is_port_dict_updated
def get_change_event(self, timeout=0):
"""
Returns a nested dictionary containing all devices which have
experienced a change at chassis level
"""
port_dict = {}
change_dict = {}
change_dict['sfp'] = port_dict
try:
# We get notified when there is a MSI interrupt (vector 4/5)CVR
# Open the sysfs file and register the epoll object
self.oir_fd = os.fdopen(os.open(self.OIR_FD_PATH, os.O_RDONLY))
if self.oir_fd != -1:
# Do a dummy read before epoll register
self.oir_fd.read()
self.epoll = select.epoll()
self.epoll.register(
self.oir_fd.fileno(), select.EPOLLIN & select.EPOLLET)
else:
print("get_transceiver_change_event : unable to create fd")
return False, change_dict
# Check for missed interrupts by invoking self._check_interrupts
# which will update the port_dict.
while True:
interrupt_count_start = self._get_register(self.OIR_FD_PATH)
retval, is_port_dict_updated = self._check_interrupts(port_dict)
if ((retval == 0) and (is_port_dict_updated is True)):
return True, change_dict
interrupt_count_end = self._get_register(self.OIR_FD_PATH)
if (interrupt_count_start == 'ERR' or
interrupt_count_end == 'ERR'):
print("get_transceiver_change_event : \
unable to retrive interrupt count")
break
# check_interrupts() itself may take upto 100s of msecs.
# We detect a missed interrupt based on the count
if interrupt_count_start == interrupt_count_end:
break
# Block until an xcvr is inserted or removed with timeout = -1
events = self.epoll.poll(
timeout=timeout if timeout != 0 else -1)
if events:
# check interrupts and return the change_dict
retval, is_port_dict_updated = \
self._check_interrupts(port_dict)
if (retval != 0):
return False, change_dict
return True, change_dict
except Exception as e:
return False, change_dict
finally:
if self.oir_fd != -1:
self.epoll.unregister(self.oir_fd.fileno())
self.epoll.close()
self.oir_fd.close()
self.oir_fd = -1
self.epoll = -1
return False, change_dict
def get_sfp(self, index):
"""
Retrieves sfp represented by (1-based) index <index>
Args:
index: An integer, the index (1-based) of the sfp to retrieve.
The index should be the sequence of a physical port in a chassis,
starting from 1.
For example, 1 for Ethernet0, 2 for Ethernet4 and so on.
Returns:
An object dervied from SfpBase representing the specified sfp
"""
sfp = None
try:
# The index will start from 1
sfp = self._sfp_list[index-1]
except IndexError:
sys.stderr.write("SFP index {} out of range (1-{})\n".format(
index, len(self._sfp_list)))
return sfp
def get_name(self):
"""
Retrieves the name of the chassis
Returns:
string: The name of the chassis
"""
return self._eeprom.modelstr()
def get_presence(self):
"""
Retrieves the presence of the chassis
Returns:
bool: True if chassis is present, False if not
"""
return True
def get_model(self):
"""
Retrieves the model number (or part number) of the chassis
Returns:
string: Model/part number of chassis
"""
return self._eeprom.part_number_str()
def get_serial(self):
"""
Retrieves the serial number of the chassis (Service tag)
Returns:
string: Serial number of chassis
"""
return self._eeprom.serial_str()
def get_status(self):
"""
Retrieves the operational status of the chassis
Returns:
bool: A boolean value, True if chassis is operating properly
False if not
"""
return True
def get_base_mac(self):
"""
Retrieves the base MAC address for the chassis
Returns:
A string containing the MAC address in the format
'XX:XX:XX:XX:XX:XX'
"""
return self._eeprom.base_mac_addr()
def get_serial_number(self):
"""
Retrieves the hardware serial number for the chassis
Returns:
A string containing the hardware serial number for this chassis.
"""
return self._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._eeprom.system_eeprom_info()
def get_reboot_cause(self):
"""
Retrieves the cause of the previous reboot
Returns:
A tuple (string, string) where the first element is a string
containing the cause of the previous reboot. This string must be
one of the predefined strings in this class. If the first string
is "REBOOT_CAUSE_HARDWARE_OTHER", the second string can be used
to pass a description of the reboot cause.
"""
try:
with open(self.REBOOT_CAUSE_PATH) as fd:
reboot_cause = int(fd.read(), 16)
except Exception as e:
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
if reboot_cause & 0x1:
return (self.REBOOT_CAUSE_POWER_LOSS, None)
elif reboot_cause & 0x2:
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
elif reboot_cause & 0x4:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "PSU Shutdown")
elif reboot_cause & 0x8:
return (self.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU, None)
elif reboot_cause & 0x10:
return (self.REBOOT_CAUSE_WATCHDOG, None)
elif reboot_cause & 0x20:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "BMC Shutdown")
elif reboot_cause & 0x40:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Hot-Swap Shutdown")
elif reboot_cause & 0x80:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Reset Button Shutdown")
elif reboot_cause & 0x100:
return (self.REBOOT_CAUSE_HARDWARE_OTHER, "Reset Button Cold Reboot")
else:
return (self.REBOOT_CAUSE_NON_HARDWARE, None)

View File

@ -0,0 +1,90 @@
#!/usr/bin/env python
########################################################################
# DELLEMC Z9264F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Components' (e.g., BIOS, CPLD, FPGA, BMC etc.) available in
# the platform
#
########################################################################
try:
import re
from sonic_platform_base.component_base import ComponentBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
FIRMWARE_VERSION_FILE="/var/log/firmware_versions"
class Component(ComponentBase):
"""DellEMC Platform-specific Component class"""
CHASSIS_COMPONENTS = [
["BIOS", ("Performs initialization of hardware components during "
"booting")],
["FPGA", ("Used for managing the system LEDs")],
["BMC", ("Platform management controller for on-board temperature "
"monitoring, in-chassis power, Fan and LED control")],
["System CPLD", ("Used for managing the CPU power sequence and CPU states")],
["Slave CPLD 1", ("Used for managing QSFP/QSFP28 port transceivers (1-16)")],
["Slave CPLD 2", ("Used for managing QSFP/QSFP28 port transceivers (17-32)")],
["Slave CPLD 3", ("Used for managing QSFP/QSFP28 port transceivers (33-48)")],
["Slave CPLD 4", ("Used for managing QSFP/QSFP28 port transceivers (49-64) and SFP/SFP28 "
"port transceivers (65 and 66)")],
]
def __init__(self, component_index=0):
self.index = component_index
self.name = self.CHASSIS_COMPONENTS[self.index][0]
self.description = self.CHASSIS_COMPONENTS[self.index][1]
def get_name(self):
"""
Retrieves the name of the component
Returns:
A string containing the name of the component
"""
return self.name
def get_description(self):
"""
Retrieves the description of the component
Returns:
A string containing the description of the component
"""
return self.description
def get_firmware_version(self):
"""
Retrieves the firmware version of the component
Returns:
A string containing the firmware version of the component
"""
rv = ""
try:
fd = open(FIRMWARE_VERSION_FILE,"r")
except IOError:
return rv
version_contents = fd.read()
fd.close()
if not version_contents:
return rv
if self.index < 8:
version = re.search(r''+self.CHASSIS_COMPONENTS[self.index][0]+':(.*)',version_contents)
if version:
rv = version.group(1).strip()
return rv
def install_firmware(self, image_path):
"""
Installs firmware to the component
Args:
image_path: A string, path to firmware image
Returns:
A boolean, True if install was successful, False if not
"""
return False

View File

@ -0,0 +1,140 @@
#!/usr/bin/env python
#############################################################################
# DellEmc Z9264F
#
# Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings:
# - the eeprom format definition
# - specific encoder/decoder if there is special need
#############################################################################
try:
import os.path
from sonic_eeprom import eeprom_tlvinfo
import binascii
except ImportError, e:
raise ImportError(str(e) + "- required module not found")
class Eeprom(eeprom_tlvinfo.TlvInfoDecoder):
def __init__(self):
self.eeprom_path = None
for b in (0, 1):
f = '/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom'.format(b)
if os.path.exists(f):
self.eeprom_path = f
break
if self.eeprom_path is None:
return
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):
"""
Returns the serial number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_SERIAL_NUMBER)
if not is_valid:
return "N/A"
return results[2]
def base_mac_addr(self):
"""
Returns the base mac address found in the system EEPROM
"""
(is_valid, t) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_MAC_BASE)
if not is_valid or t[1] != 6:
return super(TlvInfoDecoder, self).switchaddrstr(e)
return ":".join([binascii.b2a_hex(T) for T in t[2]])
def modelstr(self):
"""
Returns the Model name
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_PRODUCT_NAME)
if not is_valid:
return "N/A"
return results[2]
def part_number_str(self):
"""
Returns the part number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_PART_NUMBER)
if not is_valid:
return "N/A"
return results[2]
def serial_str(self):
"""
Returns the servicetag number
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_SERVICE_TAG)
if not is_valid:
return "N/A"
return results[2]
def revision_str(self):
"""
Returns the device revision
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_DEVICE_VERSION)
if not is_valid:
return "N/A"
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

View File

@ -0,0 +1,174 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9264F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fans' information which are available in the platform.
#
########################################################################
try:
from sonic_platform_base.fan_base import FanBase
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
#Derived the offset from BMC management information
FAN1_MAX_SPEED_OFFSET = 71
FAN2_MAX_SPEED_OFFSET = 73
PSU_FAN_MAX_SPEED_OFFSET = 50
FAN_DIRECTION_OFFSET = 69
PSU_FAN_DIRECTION_OFFSET = 47
class Fan(FanBase):
"""DellEMC Platform-specific Fan class"""
#Derived the sensor IDs from BMC
# { FAN-ID: { Sensor-Name: Sensor-ID } }
FAN_SENSOR_MAPPING = { 1: {"Prsnt": 0x51, "State": 0x64, "Speed": 0x24},
2: {"Prsnt": 0x51, "State": 0x60, "Speed": 0x20},
3: {"Prsnt": 0x52, "State": 0x65, "Speed": 0x25},
4: {"Prsnt": 0x52, "State": 0x61, "Speed": 0x21},
5: {"Prsnt": 0x53, "State": 0x66, "Speed": 0x26},
6: {"Prsnt": 0x53, "State": 0x62, "Speed": 0x22},
7: {"Prsnt": 0x54, "State": 0x67, "Speed": 0x27},
8: {"Prsnt": 0x54, "State": 0x63, "Speed": 0x23} }
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x46, "Speed": 0x2e},
2: {"State": 0x47, "Speed": 0x2f} }
# { FANTRAY-ID: FRU-ID }
FAN_FRU_MAPPING = { 1: 1, 2: 2, 3: 3, 4: 4 }
PSU_FRU_MAPPING = { 1: 6, 2: 7 }
def __init__(self, fantray_index=1, fan_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
if (self.fanindex == 1):
self.max_speed_offset = FAN1_MAX_SPEED_OFFSET
else:
self.max_speed_offset = FAN2_MAX_SPEED_OFFSET
self.fan_direction_offset = FAN_DIRECTION_OFFSET
self.index = (self.fantrayindex - 1) * 2 + self.fanindex
self.prsnt_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Prsnt"],
is_discrete=True)
self.state_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["State"],
is_discrete=True)
self.speed_sensor = IpmiSensor(self.FAN_SENSOR_MAPPING[self.index]["Speed"])
self.fru = IpmiFru(self.FAN_FRU_MAPPING[self.fantrayindex])
else:
self.dependency = dependency
self.fanindex = fan_index
self.state_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["State"],
is_discrete=True)
self.speed_sensor = IpmiSensor(self.PSU_FAN_SENSOR_MAPPING[self.fanindex]["Speed"])
self.fru = IpmiFru(self.PSU_FRU_MAPPING[self.fanindex])
self.max_speed_offset = PSU_FAN_MAX_SPEED_OFFSET
self.fan_direction_offset = PSU_FAN_DIRECTION_OFFSET
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
def get_name(self):
"""
Retrieves the name of the device
Returns:
String: The name of the device
"""
if self.is_psu_fan:
return "PSU{} Fan".format(self.fanindex)
else:
return "FanTray{}-Fan{}".format(self.fantrayindex, self.fanindex)
def get_model(self):
"""
Retrieves the part number of the FAN
Returns:
String: Part number of FAN
"""
if self.is_psu_fan:
return 'NA'
else:
return self.fru.get_board_part_number()
def get_serial(self):
"""
Retrieves the serial number of the FAN
Returns:
String: Serial number of FAN
"""
if self.is_psu_fan:
return 'NA'
else:
return self.fru.get_board_serial()
def get_presence(self):
"""
Retrieves the presence of the FAN
Returns:
bool: True if fan is present, False if not
"""
presence = False
if self.is_psu_fan:
return self.dependency.get_presence()
else:
is_valid, state = self.prsnt_sensor.get_reading()
if is_valid:
if (state & 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
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state == 0x00):
status = True
return status
def get_direction(self):
"""
Retrieves the fan airfow 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_EXHAUST, self.FAN_DIRECTION_INTAKE]
fan_status = self.get_status()
if not fan_status:
return 'NA'
is_valid, fan_direction = self.fru.get_fru_data(self.fan_direction_offset)
if is_valid:
return direction[fan_direction[0]]
else:
return 'NA'
def get_speed(self):
"""
Retrieves the speed of the fan
Returns:
int: percentage of the max fan speed
"""
if self.max_speed == 0:
self.max_speed = self.fru.get_fru_data(self.max_speed_offset,2)[1]
self.max_speed = self.max_speed[1] << 8 | self.max_speed[0]
is_valid, fan_speed = self.speed_sensor.get_reading()
if not is_valid or self.max_speed == 0:
speed = 0
else:
speed = (100 * fan_speed)/self.max_speed
return speed

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
#############################################################################
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
from sonic_platform_base.platform_base import PlatformBase
from sonic_platform.chassis import Chassis
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Platform(PlatformBase):
"""
DELLEMC Platform-specific class
"""
def __init__(self):
PlatformBase.__init__(self)
self._chassis = Chassis()

View File

@ -0,0 +1,158 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9264
#
# Module contains an implementation of SONiC Platform Base API and
# provides the PSUs' information which are available in the platform
#
########################################################################
try:
from sonic_platform_base.psu_base import PsuBase
from sonic_platform.ipmihelper import IpmiSensor, IpmiFru
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"""
# { PSU-ID: { Sensor-Name: Sensor-ID } }
SENSOR_MAPPING = { 1: { "State": 0x31, "Current": 0x39,
"Power": 0x37, "Voltage": 0x38 },
2: { "State": 0x32, "Current": 0x3F,
"Power": 0x3D, "Voltage": 0x3E } }
# ( PSU-ID: FRU-ID }
FRU_MAPPING = { 1: 6, 2: 7 }
def __init__(self, psu_index):
PsuBase.__init__(self)
# PSU is 1-based in DellEMC platforms
self.index = psu_index + 1
self.state_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["State"],
is_discrete=True)
self.voltage_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Voltage"])
self.current_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Current"])
self.power_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Power"])
self.fru = IpmiFru(self.FRU_MAPPING[self.index])
self._fan_list.append(Fan(fantray_index=1,fan_index=self.index, psu_fan=True,
dependency=self))
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
"""
presence = False
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state & 0b1):
presence = True
return presence
def get_model(self):
"""
Retrieves the part number of the PSU
Returns:
string: Part number of PSU
"""
return self.fru.get_board_part_number()
def get_serial(self):
"""
Retrieves the serial number of the PSU
Returns:
string: Serial number of PSU
"""
return self.fru.get_board_serial()
def get_status(self):
"""
Retrieves the operational status of the PSU
Returns:
bool: True if PSU is operating properly, False if not
"""
status = False
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state == 0x01):
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
"""
is_valid, voltage = self.voltage_sensor.get_reading()
if not is_valid:
voltage = 0
return float(voltage)
def get_current(self):
"""
Retrieves present electric current supplied by PSU
Returns:
A float number, electric current in amperes,
e.g. 15.4
"""
is_valid, current = self.current_sensor.get_reading()
if not is_valid:
current = 0
return float(current)
def get_power(self):
"""
Retrieves current energy supplied by PSU
Returns:
A float number, the power in watts,
e.g. 302.6
"""
is_valid, power = self.power_sensor.get_reading()
if not is_valid:
power = 0
return float(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
is_valid, state = self.state_sensor.get_reading()
if is_valid:
if (state == 0x01):
status = True
return status

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,155 @@
#!/usr/bin/env python
########################################################################
# DellEMC Z9264
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Thermals' information which are available in the platform
#
########################################################################
try:
from sonic_platform_base.thermal_base import ThermalBase
from sonic_platform.ipmihelper import IpmiSensor
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class Thermal(ThermalBase):
"""DellEMC Platform-specific Thermal class"""
# [ Sensor-Name, Sensor-ID ]
SENSOR_MAPPING = [
['CPU On-board', 0x6],
['ASIC On-board', 0x8],
['System Front Left', 0x3],
['System Front Middle', 0x7],
['System Front Right', 0x4],
['Inlet Airflow Sensor', 0x5],
['PSU1 Airflow Sensor', 0x2],
['PSU2 Airflow Sensor', 0x1]
]
def __init__(self, thermal_index):
ThermalBase.__init__(self)
self.index = thermal_index + 1
self.sensor = IpmiSensor(self.SENSOR_MAPPING[self.index - 1][1])
def get_name(self):
"""
Retrieves the name of the thermal
Returns:
string: The name of the thermal
"""
return self.SENSOR_MAPPING[self.index - 1][0]
def get_presence(self):
"""
Retrieves the presence of the thermal
Returns:
bool: True if thermal is present, False if not
"""
return True
def get_model(self):
"""
Retrieves the model number (or part number) of the Thermal
Returns:
string: Model/part number of Thermal
"""
return 'NA'
def get_serial(self):
"""
Retrieves the serial number of the Thermal
Returns:
string: Serial number of Thermal
"""
return 'NA'
def get_status(self):
"""
Retrieves the operational status of the thermal
Returns:
A boolean value, True if thermal is operating properly,
False if not
"""
return True
def get_temperature(self):
"""
Retrieves current temperature reading from thermal
Returns:
A float number of current temperature in Celsius up to
nearest thousandth of one degree Celsius, e.g. 30.125
"""
is_valid, temperature = self.sensor.get_reading()
if not is_valid:
temperature = 0
return float(temperature)
def get_high_threshold(self):
"""
Retrieves the high threshold temperature of thermal
Returns:
A float number, the high threshold temperature of thermal in
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
is_valid, high_threshold = self.sensor.get_threshold("UpperNonRecoverable")
if not is_valid:
high_threshold = 0
return float(high_threshold)
def get_low_threshold(self):
"""
Retrieves the low threshold temperature of thermal
Returns:
A float number, the low threshold temperature of thermal in
Celsius up to nearest thousandth of one degree Celsius,
e.g. 30.125
"""
is_valid, low_threshold = self.sensor.get_threshold("LowerNonRecoverable")
if not is_valid:
low_threshold = 0
return float(low_threshold)
def set_high_threshold(self, temperature):
"""
Sets the high threshold temperature of thermal
Args :
temperature: A float number up to nearest thousandth of one
degree Celsius, e.g. 30.125
Returns:
A boolean, True if threshold is set successfully, False if
not
"""
# Thermal threshold values are pre-defined based on HW.
return False
def set_low_threshold(self, temperature):
"""
Sets the low threshold temperature of thermal
Args :
temperature: A float number up to nearest thousandth of one
degree Celsius, e.g. 30.125
Returns:
A boolean, True if threshold is set successfully, False if
not
"""
# Thermal threshold values are pre-defined based on HW.
return False

View File

@ -0,0 +1,210 @@
#!/usr/bin/env python
########################################################################
#
# DELLEMC Z9264f
#
# Abstract base class for implementing a platform-specific class with
# which to interact with a hardware watchdog module in SONiC
#
########################################################################
try:
import ctypes
import subprocess
from sonic_platform_base.watchdog_base import WatchdogBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
class _timespec(ctypes.Structure):
_fields_ = [
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
]
class Watchdog(WatchdogBase):
"""
Abstract base class for interfacing with a hardware watchdog module
"""
TIMERS = [15,20,30,40,50,60,65,70]
armed_time = 0
timeout = 0
CLOCK_MONOTONIC = 1
def __init__(self):
self._librt = ctypes.CDLL('librt.so.1', use_errno=True)
self._clock_gettime = self._librt.clock_gettime
self._clock_gettime.argtypes=[ctypes.c_int, ctypes.POINTER(_timespec)]
def _get_command_result(self, cmdline):
try:
proc = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
stdout = proc.communicate()[0]
proc.wait()
result = stdout.rstrip('\n')
except OSError:
result = None
return result
def _get_reg_val(self):
# 0x31 = CPLD I2C Base Address
# 0x07 = Watchdog Function Register
value = self._get_command_result("/usr/sbin/i2cget -y 601 0x31 0x07")
if not value:
return None
else:
return int(value, 16)
def _set_reg_val(self,val):
# 0x31 = CPLD I2C Base Address
# 0x07 = Watchdog Function Register
value = self._get_command_result("/usr/sbin/i2cset -y 601 0x31 0x07 %s"
% (val))
return value
def _get_time(self):
"""
To get clock monotonic time
"""
ts = _timespec()
if self._clock_gettime(self.CLOCK_MONOTONIC, ctypes.pointer(ts)) != 0:
self._errno = ctypes.get_errno()
return 0
return ts.tv_sec + ts.tv_nsec * 1e-9
def arm(self, seconds):
"""
Arm the hardware watchdog with a timeout of <seconds> seconds.
If the watchdog is currently armed, calling this function will
simply reset the timer to the provided value. If the underlying
hardware does not support the value provided in <seconds>, this
method should arm the watchdog with the *next greater*
available value.
Returns:
An integer specifying the *actual* number of seconds the
watchdog was armed with. On failure returns -1.
"""
timer_offset = -1
for key,timer_seconds in enumerate(self.TIMERS):
if seconds <= timer_seconds:
timer_offset = key
seconds = timer_seconds
break
if timer_offset == -1:
return -1
# Extracting 5th to 7th bits for WD timer values
# 000 - 15 sec
# 001 - 20 sec
# 010 - 30 sec
# 011 - 40 sec
# 100 - 50 sec
# 101 - 60 sec
# 110 - 65 sec
# 111 - 70 sec
reg_val = self._get_reg_val()
wd_timer_offset = (reg_val >> 4) & 0x7
if wd_timer_offset != timer_offset:
# Setting 5th to 7th bits
# value from timer_offset
self.disarm()
self._set_reg_val((reg_val & 0x87) | (timer_offset << 4))
if self.is_armed():
# Setting last bit to WD Timer punch
# Last bit = WD Timer punch
self._set_reg_val(reg_val & 0xFE)
self.armed_time = self._get_time()
self.timeout = seconds
return seconds
else:
# Setting 4th bit to enable WD
# 4th bit = Enable WD
reg_val = self._get_reg_val()
self._set_reg_val(reg_val | 0x8)
self.armed_time = self._get_time()
self.timeout = seconds
return seconds
def disarm(self):
"""
Disarm the hardware watchdog
Returns:
A boolean, True if watchdog is disarmed successfully, False
if not
"""
if self.is_armed():
# Setting 4th bit to disable WD
# 4th bit = Disable WD
reg_val = self._get_reg_val()
self._set_reg_val(reg_val & 0xF7)
self.armed_time = 0
self.timeout = 0
return True
return False
def is_armed(self):
"""
Retrieves the armed state of the hardware watchdog.
Returns:
A boolean, True if watchdog is armed, False if not
"""
# Extracting 4th bit to get WD Enable/Disable status
# 0 - Disabled WD
# 1 - Enabled WD
reg_val = self._get_reg_val()
wd_offset = (reg_val >> 3) & 1
return bool(wd_offset)
def get_remaining_time(self):
"""
If the watchdog is armed, retrieve the number of seconds
remaining on the watchdog timer
Returns:
An integer specifying the number of seconds remaining on
their watchdog timer. If the watchdog is not armed, returns
-1.
Z9264 doesnot have hardware support to show remaining time.
Due to this limitation, this API is implemented in software.
This API would return correct software time difference if it
is called from the process which armed the watchdog timer.
If this API called from any other process, it would return
0. If the watchdog is not armed, this API would return -1.
"""
if not self.is_armed():
return -1
if self.armed_time > 0 and self.timeout != 0:
cur_time = self._get_time()
if cur_time <= 0:
return 0
diff_time = int(cur_time - self.armed_time)
if diff_time > self.timeout:
return self.timeout
else:
return self.timeout - diff_time
return 0