Skip to content
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

[202012]: Update Silverstone BSP and platform APIs #266

Prev Previous commit
Next Next commit
[device/celestica-silverstone]: update platform api follow lastest spec
Wirut Getbamrung committed Dec 21, 2021
commit 69b73e2f73ca1f58bedb6127ebfba158566e2799
264 changes: 242 additions & 22 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/chassis.py
Original file line number Diff line number Diff line change
@@ -6,20 +6,12 @@
#
#############################################################################

import sys
import re
import os
import subprocess
import json

try:
import sys
from sonic_platform_base.chassis_base import ChassisBase
from sonic_platform.component import Component
from sonic_platform.eeprom import Tlv
from sonic_platform.fan import Fan
from sonic_platform.sfp import Sfp
from sonic_platform.psu import Psu
from sonic_platform.thermal import Thermal
from sonic_platform_base.sonic_sfp.sfputilhelper import SfpUtilHelper
from sonic_py_common import device_info
from .event import SfpEvent
from .helper import APIHelper
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
@@ -34,35 +26,71 @@
IPMI_OEM_NETFN = "0x3A"
IPMI_GET_REBOOT_CAUSE = "0x03 0x00 0x01 0x06"

IPMI_SET_SYS_LED_CMD = "0x00 0x02 {}"
IPMI_GET_SYS_LED_CMD = "0x00 0x02"

SYS_LED_OFF_CMD = "0x00"
SYS_LED_GREEN_CMD = "0x01"
SYS_LED_AMBER_CMD = "0x02"


class Chassis(ChassisBase):
"""Platform-specific Chassis class"""

def __init__(self):
self.config_data = {}
ChassisBase.__init__(self)
self._eeprom = Tlv()
self._api_helper = APIHelper()
self.sfp_module_initialized = False

self.__initialize_eeprom()
self.is_host = self._api_helper.is_host()

self.__initialize_fan()
self.__initialize_psu()
self.__initialize_thermals()
self.__initialize_components()

for fant_index in range(0, NUM_FAN_TRAY):
for fan_index in range(0, NUM_FAN):
fan = Fan(fant_index, fan_index)
self._fan_list.append(fan)
def __initialize_sfp(self):
sfputil_helper = SfpUtilHelper()
port_config_file_path = device_info.get_path_to_port_config_file()
sfputil_helper.read_porttab_mappings(port_config_file_path, 0)

from sonic_platform.sfp import Sfp
for index in range(0, NUM_SFP):
sfp = Sfp(index)
sfp = Sfp(index, sfputil_helper.logical[index])
self._sfp_list.append(sfp)
self.sfp_module_initialized = True

def __initialize_psu(self):
from sonic_platform.psu import Psu
for index in range(0, NUM_PSU):
psu = Psu(index)
self._psu_list.append(psu)
for index in range(0, NUM_COMPONENT):
component = Component(index)
self._component_list.append(component)

def __initialize_fan(self):
from sonic_platform.fan_drawer import FanDrawer
for i in range(NUM_FAN_TRAY):
fandrawer = FanDrawer(i)
self._fan_drawer_list.append(fandrawer)
self._fan_list.extend(fandrawer._fan_list)

def __initialize_thermals(self):
from sonic_platform.thermal import Thermal
# airflow = self.__get_air_flow()
for index in range(0, NUM_THERMAL):
thermal = Thermal(index)
self._thermal_list.append(thermal)

def __initialize_eeprom(self):
from sonic_platform.eeprom import Tlv
self._eeprom = Tlv()

def __initialize_components(self):
from sonic_platform.component import Component
for index in range(0, NUM_COMPONENT):
component = Component(index)
self._component_list.append(component)

def get_base_mac(self):
"""
Retrieves the base MAC address for the chassis
@@ -129,3 +157,195 @@ def get_reboot_cause(self):
}.get(hx_cause, "Unknown reason")

return (reboot_cause, description)

# def get_change_event(self, timeout=0):
# """
# Returns a nested dictionary containing all devices which have
# experienced a change at chassis level
# Args:
# timeout: Timeout in milliseconds (optional). If timeout == 0,
# this method will block until a change is detected.
# Returns:
# (bool, dict):
# - True if call successful, False if not;
# - A nested dictionary where key is a device type,
# value is a dictionary with key:value pairs in the format of
# {'device_id':'device_event'},
# where device_id is the device ID for this device and
# device_event,
# status='1' represents device inserted,
# status='0' represents device removed.
# Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}}
# indicates that fan 0 has been removed, fan 2
# has been inserted and sfp 11 has been removed.
# """
# # SFP event
# if not self.sfp_module_initialized:
# self.__initialize_sfp()

# sfp_event = SfpEvent(self._sfp_list).get_sfp_event(timeout)
# if sfp_event:
# return True, {'sfp': sfp_event}

# return False, {'sfp': {}}

##############################################################
######################## SFP methods #########################
##############################################################

def get_num_sfps(self):
"""
Retrieves the number of sfps available on this chassis
Returns:
An integer, the number of sfps available on this chassis
"""
if not self.sfp_module_initialized:
self.__initialize_sfp()

return len(self._sfp_list)

def get_all_sfps(self):
"""
Retrieves all sfps available on this chassis
Returns:
A list of objects derived from SfpBase representing all sfps
available on this chassis
"""
if not self.sfp_module_initialized:
self.__initialize_sfp()

return self._sfp_list

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
"""
if not self.sfp_module_initialized:
self.__initialize_sfp()
return super(Chassis, self).get_sfp(index-1)

##############################################################
####################### Other methods ########################
##############################################################

def get_watchdog(self):
"""
Retreives hardware watchdog device on this chassis
Returns:
An object derived from WatchdogBase representing the hardware
watchdog device
"""
if self._watchdog is None:
from sonic_platform.watchdog import Watchdog
self._watchdog = Watchdog()

return self._watchdog

def get_thermal_manager(self):
from .thermal_manager import ThermalManager
return ThermalManager

##############################################################
###################### Device methods ########################
##############################################################

def get_name(self):
"""
Retrieves the name of the device
Returns:
string: The name of the device
"""
return self._api_helper.hwsku

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 device
Returns:
string: Model/part number of device
"""
return self._eeprom.get_pn()

def get_serial(self):
"""
Retrieves the serial number of the device
Returns:
string: Serial number of device
"""
return self._eeprom.get_serial()

def get_status(self):
"""
Retrieves the operational status of the device
Returns:
A boolean value, True if device is operating properly, False if not
"""
return True

def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position
for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned
Returns:
integer: The 1-based relative physical position in parent device or -1 if cannot determine the position
"""
return -1

def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False

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
Note: Only support green and off
Returns:
bool: True if status LED state is set successfully, False if not
"""
led_cmd = {
self.STATUS_LED_COLOR_GREEN: SYS_LED_GREEN_CMD,
self.STATUS_LED_COLOR_AMBER: SYS_LED_AMBER_CMD,
self.STATUS_LED_COLOR_OFF: SYS_LED_OFF_CMD
}.get(color)

status, set_led = self._api_helper.ipmi_raw(
IPMI_OEM_NETFN, IPMI_SET_SYS_LED_CMD.format(led_cmd))
set_status_led = False if not status else True

return set_status_led

def get_status_led(self):
"""
Gets the state of the PSU status LED
Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings above
"""
status, hx_color = self._api_helper.ipmi_raw(
IPMI_OEM_NETFN, IPMI_GET_SYS_LED_CMD)

status_led = {
"00": self.STATUS_LED_COLOR_OFF,
"01": self.STATUS_LED_COLOR_GREEN,
"02": self.STATUS_LED_COLOR_AMBER,
}.get(hx_color, self.STATUS_LED_COLOR_OFF)
return status_led
Original file line number Diff line number Diff line change
@@ -96,6 +96,29 @@ def get_firmware_version(self):

return fw_version

def get_available_firmware_version(self, image_path):
"""
Retrieves the available firmware version of the component
Note: the firmware version will be read from image
Args:
image_path: A string, path to firmware image
Returns:
A string containing the available firmware version of the component
"""
return "N/A"

def get_firmware_update_notification(self, image_path):
"""
Retrieves a notification on what should be done in order to complete
the component firmware update
Args:
image_path: A string, path to firmware image
Returns:
A string containing the component firmware update notification if required.
By default 'None' value will be used, which indicates that no actions are required
"""
return "None"

def install_firmware(self, image_path):
"""
Install firmware to module
@@ -117,3 +140,72 @@ def install_firmware(self, image_path):
# print(install_command)
status = self._api_helper.run_interactive_command(install_command)
return status

def update_firmware(self, image_path):
"""
Updates firmware of the component
This API performs firmware update: it assumes firmware installation and loading in a single call.
In case platform component requires some extra steps (apart from calling Low Level Utility)
to load the installed firmware (e.g, reboot, power cycle, etc.) - this will be done automatically by API
Args:
image_path: A string, path to firmware image
Raises:
RuntimeError: update failed
"""
return False

##############################################################
###################### Device methods ########################
##############################################################

def get_presence(self):
"""
Retrieves the presence of the FAN
Returns:
bool: True if FAN is present, False if not
"""
return True

def get_model(self):
"""
Retrieves the model number (or part number) of the device
Returns:
string: Model/part number of device
"""
return 'N/A'

def get_serial(self):
"""
Retrieves the serial number of the device
Returns:
string: Serial number of device
"""
return 'N/A'

def get_status(self):
"""
Retrieves the operational status of the device
Returns:
A boolean value, True if device is operating properly, False if not
"""
return True

def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
If the agent cannot determine the parent-relative position
for some reason, or if the associated value of
entPhysicalContainedIn is'0', then the value '-1' is returned
Returns:
integer: The 1-based relative physical position in parent device
or -1 if cannot determine the position
"""
return -1

def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
56 changes: 40 additions & 16 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/eeprom.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#############################################################################
# Celestica Silverstone
# Celestica Seastone-DX010
#
# Platform and model specific eeprom subclass, inherits from the base class,
# and provides the followings:
@@ -8,35 +8,30 @@
#############################################################################

try:
import glob
import os
import sys
import re
from array import array

if sys.version_info.major == 3:
from io import StringIO
else:
from cStringIO import StringIO

from sonic_platform_base.sonic_eeprom import eeprom_dts
from sonic_platform_base.sonic_eeprom import eeprom_tlvinfo
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

CACHE_ROOT = '/var/cache/sonic/decode-syseeprom'
CACHE_FILE = 'syseeprom_cache'
TLV_EEPROM_I2C_BUS = 0
TLV_EEPROM_I2C_ADDR = 56
NULL = 'N/A'


class Tlv(eeprom_tlvinfo.TlvInfoDecoder):

EEPROM_DECODE_HEADLINES = 6

def __init__(self):
self._eeprom_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-00{1}/eeprom".format(
TLV_EEPROM_I2C_BUS, TLV_EEPROM_I2C_ADDR)
self._eeprom_path = "/sys/class/i2c-adapter/i2c-12/12-0050/eeprom"
super(Tlv, self).__init__(self._eeprom_path, 0, '', True)
self._eeprom = self._load_eeprom()

@@ -55,7 +50,7 @@ def __parse_output(self, decode_output):
value = match.group(3).rstrip('\0')

_eeprom_info_dict[idx] = value
except:
except BaseException:
pass
return _eeprom_info_dict

@@ -64,7 +59,7 @@ def _load_eeprom(self):
sys.stdout = StringIO()
try:
self.read_eeprom_db()
except:
except BaseException:
decode_output = sys.stdout.getvalue()
sys.stdout = original_stdout
return self.__parse_output(decode_output)
@@ -76,7 +71,7 @@ def _load_eeprom(self):
if not os.path.exists(CACHE_ROOT):
try:
os.makedirs(CACHE_ROOT)
except:
except BaseException:
pass

#
@@ -85,7 +80,7 @@ def _load_eeprom(self):
#
try:
self.set_cache_name(os.path.join(CACHE_ROOT, CACHE_FILE))
except:
except BaseException:
pass

e = self.read_eeprom()
@@ -94,7 +89,7 @@ def _load_eeprom(self):

try:
self.update_cache(e)
except:
except BaseException:
pass

self.decode_eeprom(e)
@@ -107,11 +102,40 @@ def _load_eeprom(self):

return self.__parse_output(decode_output)

def _valid_tlv(self, eeprom_data):
tlvinfo_type_codes_list = [
self._TLV_CODE_PRODUCT_NAME,
self._TLV_CODE_PART_NUMBER,
self._TLV_CODE_SERIAL_NUMBER,
self._TLV_CODE_MAC_BASE,
self._TLV_CODE_MANUF_DATE,
self._TLV_CODE_DEVICE_VERSION,
self._TLV_CODE_LABEL_REVISION,
self._TLV_CODE_PLATFORM_NAME,
self._TLV_CODE_ONIE_VERSION,
self._TLV_CODE_MAC_SIZE,
self._TLV_CODE_MANUF_NAME,
self._TLV_CODE_MANUF_COUNTRY,
self._TLV_CODE_VENDOR_NAME,
self._TLV_CODE_DIAG_VERSION,
self._TLV_CODE_SERVICE_TAG,
self._TLV_CODE_VENDOR_EXT,
self._TLV_CODE_CRC_32
]

for code in tlvinfo_type_codes_list:
code_str = "0x{:X}".format(code)
eeprom_data[code_str] = eeprom_data.get(code_str, NULL)
return eeprom_data

def get_eeprom(self):
return self._eeprom
return self._valid_tlv(self._eeprom)

def get_pn(self):
return self._eeprom.get('0x22', NULL)

def get_serial(self):
return self._eeprom.get('0x23', "Undefined.")
return self._eeprom.get('0x23', NULL)

def get_mac(self):
return self._eeprom.get('0x24', "Undefined.")
return self._eeprom.get('0x24', NULL)
52 changes: 52 additions & 0 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/event.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
try:
import select
from .helper import APIHelper
from sonic_py_common.logger import Logger
except ImportError as e:
raise ImportError(repr(e) + " - required module not found")


class SfpEvent:
''' Listen to insert/remove sfp events '''

QSFP_MODPRS_IRQ = '/sys/devices/platform/dx010_cpld/qsfp_modprs_irq'
GPIO_SUS6 = "/sys/devices/platform/slx-ich.0/sci_int_gpio_sus6"

def __init__(self, sfp_list):
self._api_helper = APIHelper()
self._sfp_list = sfp_list
self._logger = Logger()

def get_sfp_event(self, timeout):
epoll = select.epoll()
port_dict = {}
timeout_sec = timeout/1000

try:
# We get notified when there is an SCI interrupt from GPIO SUS6
fd = open(self.GPIO_SUS6, "r")
fd.read()

epoll.register(fd.fileno(), select.EPOLLIN & select.EPOLLET)
events = epoll.poll(timeout=timeout_sec if timeout != 0 else -1)
if events:
# Read the QSFP ABS interrupt & status registers
port_changes = self._api_helper.read_one_line_file(
self.QSFP_MODPRS_IRQ)
changes = int(port_changes, 16)
for sfp in self._sfp_list:
change = (changes >> sfp.port_num-1) & 1
if change == 1:
port_dict[str(sfp.port_num)] = str(
int(sfp.get_presence()))

return port_dict
except Exception as e:
self._logger.log_error("Failed to detect SfpEvent - " + repr(e))
return False

finally:
fd.close()
epoll.close()

return False
27 changes: 26 additions & 1 deletion device/celestica/x86_64-cel_silverstone-r0/sonic_platform/fan.py
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@ def get_speed(self):
max_rpm = MAX_OUTLET if self.fan_index % 2 == 0 else MAX_INLET
fan1_ss_start = FAN1_FRONT_SS_ID if self.fan_index % 2 == 0 else FAN1_REAR_SS_ID

ss_id = hex(int(fan1_ss_start, 16) + self.fan_tray_index) if not self.psu_index else hex(
ss_id = hex(int(fan1_ss_start, 16) + self.fan_tray_index) if not self.is_psu_fan else hex(
int(PSU_FAN1_FRONT_SS_ID, 16) + self.fan_tray_index)
status, raw_ss_read = self._api_helper.ipmi_raw(
IPMI_SENSOR_NETFN, IPMI_FAN_SPEED_CMD.format(ss_id))
@@ -224,6 +224,10 @@ def get_status_led(self):

return status_led

##############################################################
###################### Device methods ########################
##############################################################

def get_name(self):
"""
Retrieves the name of the device
@@ -299,3 +303,24 @@ def get_status(self):
A boolean value, True if device is operating properly, False if not
"""
return self.get_presence() and self.get_speed() > 0

def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
If the agent cannot determine the parent-relative position
for some reason, or if the associated value of
entPhysicalContainedIn is'0', then the value '-1' is returned
Returns:
integer: The 1-based relative physical position in parent device
or -1 if cannot determine the position
"""
return (self.fan_tray_index*2 + self.fan_index + 1) \
if not self.is_psu_fan else (self.fan_index+1)

def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True if not self.is_psu_fan else False
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python

#############################################################################
# Celestica
#
# Module contains an implementation of SONiC Platform Base API and
# provides the the Fan-Drawers' information available in the platform
#
#############################################################################

try:
from sonic_platform_base.fan_drawer_base import FanDrawerBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

NUM_FAN = 2


class FanDrawer(FanDrawerBase):
def __init__(self, fantray_index):
FanDrawerBase.__init__(self)
self._index = fantray_index + 1
self._init_fan(fantray_index)

def _init_fan(self, fantray_index):
from sonic_platform.fan import Fan
for index in range(NUM_FAN):
fan = Fan(fantray_index, index)
self._fan_list.append(fan)

def set_status_led(self, color):
"""
Sets the state of the fan drawer status LED
Args:
color: A string representing the color with which to set the
fan drawer status LED
Returns:
bool: True if status LED state is set successfully, False if not
"""
return self._fan_list[0].set_status_led(color)

def get_status_led(self, color=None):
"""
Gets the state of the fan drawer LED
Returns:
A string, one of the predefined STATUS_LED_COLOR_* strings above
"""
return self._fan_list[0].get_status_led()

##############################################################
###################### Device methods ########################
##############################################################

def get_name(self):
"""
Retrieves the name of the device
Returns:
string: The name of the device
"""
return "Drawer{}".format(self._index)

def get_presence(self):
"""
Retrieves the presence of the device
Returns:
bool: True if device is present, False if not
"""
return self._fan_list[0].get_presence()

def get_model(self):
"""
Retrieves the model number (or part number) of the device
Returns:
string: Model/part number of device
"""
return self._fan_list[0].get_model()

def get_serial(self):
"""
Retrieves the serial number of the device
Returns:
string: Serial number of device
"""
return self._fan_list[0].get_serial()

def get_status(self):
"""
Retrieves the operational status of the device
Returns:
A boolean value, True if device is operating properly, False if not
"""
return self._fan_list[0].get_status()

def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device
Returns:
integer: The 1-based relative physical position in parent device
"""
return self._index

def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True
Original file line number Diff line number Diff line change
@@ -3,14 +3,16 @@
import subprocess
from mmap import *

from sonic_py_common import device_info

HOST_CHK_CMD = "docker > /dev/null 2>&1"
EMPTY_STRING = ""


class APIHelper():

def __init__(self):
pass
(self.platform, self.hwsku) = device_info.get_platform_and_hwsku()

def is_host(self):
return os.system(HOST_CHK_CMD) == 0
@@ -57,6 +59,28 @@ def read_txt_file(self, file_path):
pass
return None

def read_one_line_file(self, file_path):
try:
with open(file_path, 'r') as fd:
data = fd.readline()
return data.strip()
except IOError:
pass
return None

def write_txt_file(self, file_path, value):
try:
with open(file_path, 'w') as fd:
fd.write(str(value))
except Exception:
return False
return True

def get_cpld_reg_value(self, getreg_path, register):
cmd = "echo {1} > {0}; cat {0}".format(getreg_path, register)
status, result = self.run_command(cmd)
return result if status else None

def ipmi_raw(self, netfn, cmd):
status = True
result = ""
@@ -95,7 +119,8 @@ def ipmi_set_ss_thres(self, id, threshold_key, value):
status = True
result = ""
try:
cmd = "ipmitool sensor thresh '{}' {} {}".format(str(id), str(threshold_key), str(value))
cmd = "ipmitool sensor thresh '{}' {} {}".format(
str(id), str(threshold_key), str(value))
p = subprocess.Popen(
cmd, shell=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
raw_data, err = p.communicate()
73 changes: 68 additions & 5 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/psu.py
Original file line number Diff line number Diff line change
@@ -6,10 +6,8 @@
#
#############################################################################

import os
import re
import math
import sonic_platform

try:
from sonic_platform_base.psu_base import PsuBase
@@ -36,12 +34,12 @@
PSU1_VOUT_SS_ID = "0x36"
PSU1_COUT_SS_ID = "0x37"
PSU1_POUT_SS_ID = "0x38"
PSU1_STATUS_REG = "0x39"
PSU1_STATUS_REG = "0x2f"

PSU2_VOUT_SS_ID = "0x40"
PSU2_COUT_SS_ID = "0x41"
PSU2_POUT_SS_ID = "0x42"
PSU2_STATUS_REG = "0x2f"
PSU2_STATUS_REG = "0x39"

PSU1_FRU_ID = 3

@@ -161,6 +159,55 @@ def get_status_led(self):

return status_led

# def get_temperature(self):
# """
# Retrieves current temperature reading from PSU
# Returns:
# A float number of current temperature in Celsius up to nearest thousandth
# of one degree Celsius, e.g. 30.125
# """
# raise NotImplementedError

# def get_temperature_high_threshold(self):
# """
# Retrieves the high threshold temperature of PSU
# Returns:
# A float number, the high threshold temperature of PSU in Celsius
# up to nearest thousandth of one degree Celsius, e.g. 30.125
# """
# raise NotImplementedError

# def get_voltage_high_threshold(self):
# """
# Retrieves the high threshold PSU voltage output
# Returns:
# A float number, the high threshold output voltage in volts,
# e.g. 12.1
# """
# raise NotImplementedError

# def get_voltage_low_threshold(self):
# """
# Retrieves the low threshold PSU voltage output
# Returns:
# A float number, the low threshold output voltage in volts,
# e.g. 12.1
# """
# raise NotImplementedError

# def get_maximum_supplied_power(self):
# """
# Retrieves the maximum supplied power by PSU
# Returns:
# A float number, the maximum power output in Watts.
# e.g. 1200.1
# """
# raise NotImplementedError

##############################################################
###################### Device methods ########################
##############################################################

def get_name(self):
"""
Retrieves the name of the device
@@ -232,10 +279,26 @@ def get_status(self):
status, raw_status_read = self._api_helper.ipmi_raw(
IPMI_SENSOR_NETFN, IPMI_SS_READ_CMD.format(psu_pstatus_key))
status_byte = self.find_value(raw_status_read)

if status:
failure_detected = (int(status_byte, 16) >> 1) & 1
input_lost = (int(status_byte, 16) >> 3) & 1
psu_status = False if (input_lost or failure_detected) else True

return psu_status

def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device. If the agent cannot determine the parent-relative position
for some reason, or if the associated value of entPhysicalContainedIn is '0', then the value '-1' is returned
Returns:
integer: The 1-based relative physical position in parent device or -1 if cannot determine the position
"""
return -1

def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True
1,455 changes: 1,141 additions & 314 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/sfp.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -31,20 +31,34 @@ def __init__(self, thermal_index):
self._api_helper = APIHelper()
self.index = thermal_index
self.THERMAL_LIST = [
('TEMP_FAN_U52', 'Fan Tray Middle Temperature Sensor', '0x00'),
('TEMP_FAN_U17', 'Fan Tray Right Temperature Sensor', '0x01'),
('TEMP_SW_U52', 'Switchboard Left Inlet Temperature Sensor', '0x02'),
('TEMP_SW_U16', 'Switchboard Right Inlet Temperature Sensor', '0x03'),
('TEMP_BB_U3', 'Baseboard Temperature Sensor', '0x04'),
('TEMP_CPU', 'CPU Internal Temperature Sensor', '0x05'),
('TEMP_SW_Internal', 'ASIC Internal Temperature Sensor', '0x61'),
('SW_U04_Temp', 'IR3595 Chip Left Temperature Sensor', '0x4F'),
('SW_U14_Temp', 'IR3595 Chip Right Temperature Sensor', '0x56'),
('SW_U4403_Temp', 'IR3584 Chip Temperature Sensor', '0x5D'),
('TEMP_FAN_U52', 'Fan Tray Middle Temperature Sensor',
'0x00', 'cpu'),
('TEMP_FAN_U17', 'Fan Tray Right Temperature Sensor',
'0x01', 'cpu'),
('TEMP_SW_U52',
'Switchboard Left Inlet Temperature Sensor', '0x02', 'asic'),
('TEMP_SW_U16',
'Switchboard Right Inlet Temperature Sensor', '0x03', 'asic'),
('TEMP_BB_U3', 'Baseboard Temperature Sensor',
'0x04', 'cpu'),
('TEMP_CPU', 'CPU Internal Temperature Sensor',
'0x05', 'cpu'),
('TEMP_SW_Internal', 'ASIC Internal Temperature Sensor',
'0x61', 'asic'),
('SW_U04_Temp', 'IR3595 Chip Left Temperature Sensor',
'0x4F', 'asic'),
('SW_U14_Temp', 'IR3595 Chip Right Temperature Sensor',
'0x56', 'asic'),
('SW_U4403_Temp', 'IR3584 Chip Temperature Sensor',
'0x5D', 'asic'),
]
self.sensor_id = self.THERMAL_LIST[self.index][0]
self.sensor_des = self.THERMAL_LIST[self.index][1]
self.sensor_reading_addr = self.THERMAL_LIST[self.index][2]
self.position = self.THERMAL_LIST[self.index][3]

self.minimum_thermal = self.get_temperature()
self.maximum_thermal = self.get_temperature()

def __set_threshold(self, key, value):
print('{} {}'.format(key, value))
@@ -97,7 +111,8 @@ def set_high_threshold(self, temperature):
Returns:
A boolean, True if threshold is set successfully, False if not
"""
status, ret_txt = self._api_helper.ipmi_set_ss_thres(self.sensor_id, HIGH_TRESHOLD_SET_KEY, temperature)
status, ret_txt = self._api_helper.ipmi_set_ss_thres(
self.sensor_id, HIGH_TRESHOLD_SET_KEY, temperature)
return status

def set_low_threshold(self, temperature):
@@ -111,6 +126,49 @@ def set_low_threshold(self, temperature):
"""
return False

def get_high_critical_threshold(self):
"""
Retrieves the high critical threshold temperature of thermal
Returns:
A float number, the high critical threshold temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
high_critical_threshold = 0.0
status, raw_up_thres_read = self._api_helper.ipmi_raw(
IPMI_SENSOR_NETFN, IPMI_SS_THRESHOLD_CMD.format(self.sensor_reading_addr))
if status and len(raw_up_thres_read.split()) > 6:
ss_read = raw_up_thres_read.split()[5]
high_critical_threshold = float(int(ss_read, 16))
return high_critical_threshold

def get_minimum_recorded(self):
"""
Retrieves the minimum recorded temperature of thermal
Returns:
A float number, the minimum recorded temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
tmp = self.get_temperature()
if tmp < self.minimum_thermal:
self.minimum_thermal = tmp
return self.minimum_thermal

def get_maximum_recorded(self):
"""
Retrieves the maximum recorded temperature of thermal
Returns:
A float number, the maximum recorded temperature of thermal in Celsius
up to nearest thousandth of one degree Celsius, e.g. 30.125
"""
tmp = self.get_temperature()
if tmp > self.maximum_thermal:
self.maximum_thermal = tmp
return self.maximum_thermal

##############################################################
###################### Device methods ########################
##############################################################

def get_name(self):
"""
Retrieves the name of the thermal device
@@ -150,3 +208,21 @@ def get_status(self):
A boolean value, True if device is operating properly, False if not
"""
return self.get_presence()

def is_replaceable(self):
"""
Retrieves whether thermal module is replaceable
Returns:
A boolean value, True if replaceable, False if not
"""
return False

def get_position_in_parent(self):
"""
Retrieves the thermal position information
Returns:
A int value, 0 represent ASIC thermal, 1 represent CPU thermal info
"""
if self.position == "cpu":
return 1
return 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from sonic_platform_base.sonic_thermal_control.thermal_manager_base import ThermalManagerBase


class ThermalManager(ThermalManagerBase):
@classmethod
def initialize(cls):
"""
Initialize thermal manager, including register thermal condition types and thermal action types
and any other vendor specific initialization.
:return:
"""
return True

@classmethod
def deinitialize(cls):
"""
Destroy thermal manager, including any vendor specific cleanup. The default behavior of this function
is a no-op.
:return:
"""
return True
180 changes: 180 additions & 0 deletions device/celestica/x86_64-cel_silverstone-r0/sonic_platform/watchdog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#############################################################################
# Celestica Seastone2
#
# Watchdog contains an implementation of SONiC Platform Base API
#
#############################################################################
import os
import time

try:
from sonic_platform_base.watchdog_base import WatchdogBase
from .helper import APIHelper
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

PLATFORM_CPLD_PATH = '/sys/devices/platform/baseboard-lpc/'
GETREG_FILE = 'getreg'
SETREG_FILE = 'setreg'
WDT_TIMER_REG = '0XA181'
WDT_ENABLE_REG = '0xA182'
WDT_KEEP_ALVIVE_REG = '0xA184'
ENABLE_CMD = '0x1'
DISABLE_CMD = '0x0'
WDT_COMMON_ERROR = -1


class Watchdog(WatchdogBase):

def __init__(self):
WatchdogBase.__init__(self)

self._api_helper = APIHelper()

# Init cpld reg path
self.setreg_path = os.path.join(PLATFORM_CPLD_PATH, SETREG_FILE)
self.getreg_path = os.path.join(PLATFORM_CPLD_PATH, GETREG_FILE)

# Set default value
self._disable()
self.armed = False
self.timeout = self._gettimeout()

def _enable(self):
"""
Turn on the watchdog timer
"""
# echo 0xA182 0x1 > /sys/devices/platform/baseboard-lpc/setreg
enable_val = '{} {}'.format(WDT_ENABLE_REG, ENABLE_CMD)
return self._api_helper.write_txt_file(self.setreg_path, enable_val)

def _disable(self):
"""
Turn off the watchdog timer
"""
# echo 0xA182 0x0 > /sys/devices/platform/baseboard-lpc/setreg
disable_val = '{} {}'.format(WDT_ENABLE_REG, DISABLE_CMD)
return self._api_helper.write_txt_file(self.setreg_path, disable_val)

def _keepalive(self):
"""
Keep alive watchdog timer
"""
# echo 0xA184 0x1 > /sys/devices/platform/baseboard-lpc/setreg
enable_val = '{} {}'.format(WDT_KEEP_ALVIVE_REG, ENABLE_CMD)
return self._api_helper.write_txt_file(self.setreg_path, enable_val)

def _settimeout(self, seconds):
"""
Set watchdog timer timeout
@param seconds - timeout in seconds
@return is the actual set timeout
"""
second_int = {
30: 1,
60: 2,
180: 3,
240: 4,
300: 5,
420: 6,
600: 7,
}.get(seconds)

self._api_helper.write_txt_file(self.setreg_path, second_int)
return seconds

def _gettimeout(self):
"""
Get watchdog timeout
@return watchdog timeout
"""
hex_str = self._api_helper.get_cpld_reg_value(
self.getreg_path, WDT_TIMER_REG)
bin_val = bin(int(hex_str, 16))[2:].zfill(3)

return {
'001': 30,
'010': 60,
'011': 180,
'100': 240,
'101': 300,
'110': 420,
'111': 600
}.get(bin_val)

#################################################################

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.
"""

avaliable_second = [30, 60, 180, 240, 300, 420, 600]
ret = WDT_COMMON_ERROR

if seconds < 0 or seconds not in avaliable_second:
return ret

try:
if self.timeout != seconds:
self.timeout = self._settimeout(seconds)

if self.armed:
self._keepalive()
else:
self._enable()
self.armed = True
print("read armed")

ret = self.timeout
self.arm_timestamp = time.time()
except IOError as e:
print(e)
pass

return ret

def disarm(self):
"""
Disarm the hardware watchdog
Returns:
A boolean, True if watchdog is disarmed successfully, False if not
"""
disarmed = False
if self.is_armed():
try:
self._disable()
self.armed = False
disarmed = True
except IOError:
pass

return disarmed

def is_armed(self):
"""
Retrieves the armed state of the hardware watchdog.
Returns:
A boolean, True if watchdog is armed, False if not
"""

return self.armed

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 thei
watchdog timer. If the watchdog is not armed, returns -1.
"""

return int(self.timeout - (time.time() - self.arm_timestamp)) if self.armed else WDT_COMMON_ERROR