diff --git a/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c b/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c index 141f41ba4a03..7e725a35a8ad 100644 --- a/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c +++ b/platform/broadcom/sonic-platform-modules-dell/common/dell_pmc.c @@ -112,7 +112,7 @@ #define PSU_1_OUTPUT_CURRENT 0x0244 #define PSU_1_INPUT_POWER 0x0246 #define PSU_1_OUTPUT_POWER 0x0248 -#define PSU_1_FAN_SPEED 0x023B +#define PSU_1_COUNTRY_CODE 0x024A /* PSU2 */ #define PSU_2_MAX_POWER 0x026D @@ -127,6 +127,7 @@ #define PSU_2_OUTPUT_CURRENT 0x027D #define PSU_2_INPUT_POWER 0x027F #define PSU_2_OUTPUT_POWER 0x0281 +#define PSU_2_COUNTRY_CODE 0x0283 /* TEMP */ #define TEMP_SENSOR_1 0x0014 @@ -151,6 +152,20 @@ #define CPU_7_MONITOR_STATUS 0x02E8 #define CPU_8_MONITOR_STATUS 0x02E9 +/* EEPROM PPID */ +/* FAN and PSU EEPROM PPID format is: + COUNTRY_CODE-PART_NO-MFG_ID-MFG_DATE_CODE-SERIAL_NO-LABEL_REV */ +#define EEPROM_COUNTRY_CODE_SIZE 2 +#define EEPROM_PART_NO_SIZE 6 +#define EEPROM_MFG_ID_SIZE 5 +#define EEPROM_MFG_DATE_SIZE 8 +#define EEPROM_DATE_CODE_SIZE 3 +#define EEPROM_SERIAL_NO_SIZE 4 +#define EEPROM_SERVICE_TAG_SIZE 7 +#define EEPROM_LABEL_REV_SIZE 3 +#define EEPROM_PPID_SIZE 28 + + unsigned long *mmio; static struct kobject *dell_kobj; @@ -1721,6 +1736,118 @@ static ssize_t show_psu(struct device *dev, return sprintf(buf, "%u\n", pow); } +/* FAN and PSU EEPROM PPID format is: + COUNTRY_CODE-PART_NO-MFG_ID-MFG_DATE_CODE-SERIAL_NO-LABEL_REV */ +static ssize_t show_psu_ppid(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct smf_data *data = dev_get_drvdata(dev); + char psu_ppid[EEPROM_PPID_SIZE + 1] = {0}; + char psu_mfg_date[EEPROM_MFG_DATE_SIZE + 1] = {0}; + char psu_mfg_date_code[EEPROM_DATE_CODE_SIZE + 1] = {0}; + char temp; + int i, reg, ret = 0, ppid_pos = 0; + + switch(index) { + + case 1: + // PPID starts from Country Code + reg = PSU_1_COUNTRY_CODE; + break; + case 2: + reg = PSU_2_COUNTRY_CODE; + break; + default: + return ret; + } + + // Get Country Code + for( i = 0; i < EEPROM_COUNTRY_CODE_SIZE; i++) { + psu_ppid[ppid_pos++] = (char)smf_read_reg(data,reg++); + } + psu_ppid[ppid_pos++] = '-'; + + // Get Part Number + for( i = 0; i < EEPROM_PART_NO_SIZE; i++) { + psu_ppid[ppid_pos++] = (char)smf_read_reg(data,reg++); + } + psu_ppid[ppid_pos++] = '-'; + + // Get Manufacture ID + for( i = 0; i < EEPROM_MFG_ID_SIZE; i++) { + psu_ppid[ppid_pos++] = (char)smf_read_reg(data,reg++); + } + psu_ppid[ppid_pos++] = '-'; + + // Get Manufacture date + for( i = 0; i < EEPROM_MFG_DATE_SIZE; i++) { + psu_mfg_date[i] = (char)smf_read_reg(data,reg++); + } + + /* Converting 6 digit date code [yymmdd] to 3 digit[ymd] + Year Starting from 2010 [0-9] , Day : 1-9 and A-V , Month : 1-9 and A-C */ + // Year Validation and Conversion + if( ( psu_mfg_date[0] == '1' ) && ( psu_mfg_date[1] >= '0' ) && ( psu_mfg_date[1] <= '9') ) + { + psu_mfg_date_code[0] = psu_mfg_date[1]; + } + else + { + psu_mfg_date_code[0] = ' '; + } + + // Month Validation and Conversion + temp = ( ( psu_mfg_date[2] - 0x30 ) * 10 ) + ( psu_mfg_date[3] - 0x30 ); + if( ( temp >= 1) && ( temp < 10) ) + { + psu_mfg_date_code[1] = temp + 0x30; // 0- 9 + } + else if ( ( temp >= 10) && ( temp <= 12) ) + { + psu_mfg_date_code[1] = temp + 0x37; // A-C + } + else + { + psu_mfg_date_code[1]= ' '; + } + + // Date Validation and Conversion + temp = ( ( psu_mfg_date[4] - 0x30 ) * 10 ) + ( psu_mfg_date[5] - 0x30 ); + if( ( temp >= 1) && ( temp < 10) ) + { + psu_mfg_date_code[2] = temp + 0x30; // 0- 9 + } + else if( ( temp >= 10) && ( temp <= 31) ) + { + psu_mfg_date_code[2] = temp + 0x37; // A-V + } + else + { + psu_mfg_date_code[2] = ' '; + } + + for( i = 0; i < EEPROM_DATE_CODE_SIZE; i++) { + psu_ppid[ppid_pos++] = psu_mfg_date_code[i]; + } + psu_ppid[ppid_pos++] = '-'; + + // Get Serial Number + for( i = 0; i < EEPROM_SERIAL_NO_SIZE; i++) { + psu_ppid[ppid_pos++] = (char)smf_read_reg(data,reg++); + } + psu_ppid[ppid_pos++] = '-'; + + // Skipping service tag in PPID + reg += EEPROM_SERVICE_TAG_SIZE; + + // Get Label Revision + for( i = 0; i < EEPROM_LABEL_REV_SIZE; i++) { + psu_ppid[ppid_pos++] = (char)smf_read_reg(data,reg++); + } + + return sprintf(buf, "%s\n",psu_ppid); +} static umode_t smf_psu_is_visible(struct kobject *kobj, struct attribute *a, int n) @@ -1803,6 +1930,8 @@ static SENSOR_DEVICE_ATTR(iom_presence, S_IRUGO, show_voltage, NULL, 45); static SENSOR_DEVICE_ATTR(psu1_presence, S_IRUGO, show_psu, NULL, 1); static SENSOR_DEVICE_ATTR(psu2_presence, S_IRUGO, show_psu, NULL, 6); +static SENSOR_DEVICE_ATTR(psu1_serialno, S_IRUGO, show_psu_ppid, NULL, 1); +static SENSOR_DEVICE_ATTR(psu2_serialno, S_IRUGO, show_psu_ppid, NULL, 2); static SENSOR_DEVICE_ATTR(current_total_power, S_IRUGO, show_psu, NULL, 10); /* SMF Version */ @@ -1833,6 +1962,8 @@ static struct attribute *smf_dell_attrs[] = { &sensor_dev_attr_iom_presence.dev_attr.attr, &sensor_dev_attr_psu1_presence.dev_attr.attr, &sensor_dev_attr_psu2_presence.dev_attr.attr, + &sensor_dev_attr_psu1_serialno.dev_attr.attr, + &sensor_dev_attr_psu2_serialno.dev_attr.attr, &sensor_dev_attr_current_total_power.dev_attr.attr, NULL }; diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py index 6245f8efdc0d..ef229bd791e2 100755 --- a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/chassis.py @@ -11,9 +11,14 @@ try: import os from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.psu import Psu + from sonic_platform.fan import Fan except ImportError as e: raise ImportError(str(e) + "- required module not found") +MAX_S6100_FAN = 4 +MAX_S6100_PSU = 2 + class Chassis(ChassisBase): """ @@ -39,19 +44,28 @@ class Chassis(ChassisBase): def __init__(self): ChassisBase.__init__(self) + for i in range(MAX_S6100_FAN): + fan = Fan(i) + self._fan_list.append(fan) + + for i in range(MAX_S6100_PSU): + psu = Psu(i) + self._psu_list.append(psu) + def get_pmc_register(self, reg_name): + # On successful read, returns the value read from given + # reg_name and on failure returns 'ERR' rv = 'ERR' - mb_reg_file = self.MAILBOX_DIR+'/'+reg_name + mb_reg_file = self.MAILBOX_DIR + '/' + reg_name if (not os.path.isfile(mb_reg_file)): - print mb_reg_file, 'not found !' return rv try: with open(mb_reg_file, 'r') as fd: rv = fd.read() except Exception as error: - logging.error("Unable to open ", mb_reg_file, "file !") + rv = 'ERR' rv = rv.rstrip('\r\n') rv = rv.lstrip(" ") diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py new file mode 100644 index 000000000000..696a9c0dd70b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/fan.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +######################################################################## +# DellEMC +# +# Module contains an implementation of SONiC Platform Base API and +# provides the Fans' information which are available in the platform +# +######################################################################## + + +try: + import os + from sonic_platform_base.fan_base import FanBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +MAX_S6100_PSU_FAN_SPEED = 18000 +MAX_S6100_FAN_SPEED = 16000 + + +class Fan(FanBase): + """DellEMC Platform-specific FAN class""" + + HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/" + HWMON_NODE = os.listdir(HWMON_DIR)[0] + MAILBOX_DIR = HWMON_DIR + HWMON_NODE + + def __init__(self, fan_index, psu_fan=False): + self.is_psu_fan = psu_fan + if not self.is_psu_fan: + # Fan is 1-based in DellEMC platforms + self.index = fan_index + 1 + self.get_fan_speed_reg = "fan{}_input".format(2*self.index - 1) + self.max_fan_speed = MAX_S6100_FAN_SPEED + else: + # PSU Fan index starts from 11 + self.index = fan_index + 10 + self.get_fan_speed_reg = "fan{}_input".format(self.index) + self.max_fan_speed = MAX_S6100_PSU_FAN_SPEED + diff --git a/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py new file mode 100644 index 000000000000..39ccf1b65500 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/s6100/sonic_platform/psu.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python + +######################################################################## +# DellEMC +# +# Module contains an implementation of SONiC Platform Base API and +# provides the PSUs' information which are available in the platform +# +######################################################################## + + +try: + import os + from sonic_platform_base.psu_base import PsuBase + from sonic_platform.fan import Fan +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Psu(PsuBase): + """DellEMC Platform-specific PSU class""" + + HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/" + HWMON_NODE = os.listdir(HWMON_DIR)[0] + MAILBOX_DIR = HWMON_DIR + HWMON_NODE + + def __init__(self, psu_index): + # PSU is 1-based in DellEMC platforms + self.index = psu_index + 1 + self.psu_presence_reg = "psu{}_presence".format(self.index) + self.psu_serialno_reg = "psu{}_serialno".format(self.index) + if self.index == 1: + self.psu_voltage_reg = "in30_input" + self.psu_current_reg = "curr602_input" + self.psu_power_reg = "power2_input" + elif self.index == 2: + self.psu_voltage_reg = "in32_input" + self.psu_current_reg = "curr702_input" + self.psu_power_reg = "power4_input" + + # Overriding _fan_list class variable defined in PsuBase, to + # make it unique per Psu object + self._fan_list = [] + + # Passing True to specify it is a PSU fan + psu_fan = Fan(self.index, True) + self._fan_list.append(psu_fan) + + def get_pmc_register(self, reg_name): + # On successful read, returns the value read from given + # reg_name and on failure returns 'ERR' + rv = 'ERR' + mb_reg_file = self.MAILBOX_DIR + '/' + reg_name + + if (not os.path.isfile(mb_reg_file)): + return rv + + try: + with open(mb_reg_file, 'r') as fd: + rv = fd.read() + except Exception as error: + rv = 'ERR' + + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return "PSU{}".format(self.index) + + def get_presence(self): + """ + Retrieves the presence of the Power Supply Unit (PSU) + + Returns: + bool: True if PSU is present, False if not + """ + status = False + psu_presence = self.get_pmc_register(self.psu_presence_reg) + if (psu_presence != 'ERR'): + psu_presence = int(psu_presence, 16) + # Checking whether bit 0 is not set + if (~psu_presence & 0b1): + status = True + + return status + + def get_model(self): + """ + Retrieves the part number of the PSU + + Returns: + string: Part number of PSU + """ + # For Serial number "US-01234D-54321-25A-0123-A00", the part + # number is "01234D" + psu_serialno = self.get_pmc_register(self.psu_serialno_reg) + if (psu_serialno != 'ERR') and self.get_presence(): + if (len(psu_serialno.split('-')) > 1): + psu_partno = psu_serialno.split('-')[1] + else: + psu_partno = 'NA' + else: + psu_partno = 'NA' + + return psu_partno + + def get_serial(self): + """ + Retrieves the serial number of the PSU + + Returns: + string: Serial number of PSU + """ + # Sample Serial number format "US-01234D-54321-25A-0123-A00" + psu_serialno = self.get_pmc_register(self.psu_serialno_reg) + if (psu_serialno == 'ERR') or not self.get_presence(): + psu_serialno = 'NA' + + return psu_serialno + + def get_status(self): + """ + Retrieves the operational status of the PSU + + Returns: + bool: True if PSU is operating properly, False if not + """ + status = False + psu_status = self.get_pmc_register(self.psu_presence_reg) + if (psu_status != 'ERR'): + psu_status = int(psu_status, 16) + # Checking whether both bit 3 and bit 2 are not set + if (~psu_status & 0b1000) and (~psu_status & 0b0100): + status = True + + return status + + def get_voltage(self): + """ + Retrieves current PSU voltage output + + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + psu_voltage = self.get_pmc_register(self.psu_voltage_reg) + if (psu_voltage != 'ERR') and self.get_presence(): + # Converting the value returned by driver which is in + # millivolts to volts + psu_voltage = float(psu_voltage) / 1000 + else: + psu_voltage = 0.0 + + return psu_voltage + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + + Returns: + A float number, electric current in amperes, + e.g. 15.4 + """ + psu_current = self.get_pmc_register(self.psu_current_reg) + if (psu_current != 'ERR') and self.get_presence(): + # Converting the value returned by driver which is in + # milliamperes to amperes + psu_current = float(psu_current) / 1000 + else: + psu_current = 0.0 + + return psu_current + + def get_power(self): + """ + Retrieves current energy supplied by PSU + + Returns: + A float number, the power in watts, + e.g. 302.6 + """ + psu_power = self.get_pmc_register(self.psu_power_reg) + if (psu_power != 'ERR') and self.get_presence(): + # Converting the value returned by driver which is in + # microwatts to watts + psu_power = float(psu_power) / 1000000 + else: + psu_power = 0.0 + + return psu_power + + def set_status_led(self): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the + PSU status LED + Returns: + bool: True if status LED state is set successfully, False if + not + """ + # In S6100, SmartFusion FPGA controls the PSU LED and the PSU + # LED state cannot be changed from CPU. + return False diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/__init__.py b/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/__init__.py deleted file mode 120000 index 9972084d4dfb..000000000000 --- a/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../s6100/sonic_platform/__init__.py \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/__init__.py b/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/__init__.py new file mode 100755 index 000000000000..139597f9cb07 --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/__init__.py @@ -0,0 +1,2 @@ + + diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/chassis.py deleted file mode 120000 index 06fba1ae4f69..000000000000 --- a/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/chassis.py +++ /dev/null @@ -1 +0,0 @@ -../../s6100/sonic_platform/chassis.py \ No newline at end of file diff --git a/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/chassis.py b/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/chassis.py new file mode 100755 index 000000000000..8e53ec81559b --- /dev/null +++ b/platform/broadcom/sonic-platform-modules-dell/z9100/sonic_platform/chassis.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +############################################################################# +# DELLEMC +# +# Module contains an implementation of SONiC Platform Base API and +# provides the platform information +# +############################################################################# + +try: + import os + from sonic_platform_base.chassis_base import ChassisBase +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +class Chassis(ChassisBase): + """ + DELLEMC Platform-specific Chassis class + """ + + HWMON_DIR = "/sys/devices/platform/SMF.512/hwmon/" + HWMON_NODE = os.listdir(HWMON_DIR)[0] + MAILBOX_DIR = HWMON_DIR + HWMON_NODE + + reset_reason_dict = {} + reset_reason_dict[11] = ChassisBase.REBOOT_CAUSE_POWER_LOSS + reset_reason_dict[33] = ChassisBase.REBOOT_CAUSE_WATCHDOG + reset_reason_dict[44] = ChassisBase.REBOOT_CAUSE_NON_HARDWARE + reset_reason_dict[55] = ChassisBase.REBOOT_CAUSE_NON_HARDWARE + + power_reason_dict = {} + power_reason_dict[11] = ChassisBase.REBOOT_CAUSE_POWER_LOSS + power_reason_dict[22] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_CPU + power_reason_dict[33] = ChassisBase.REBOOT_CAUSE_THERMAL_OVERLOAD_ASIC + power_reason_dict[44] = ChassisBase.REBOOT_CAUSE_INSUFFICIENT_FAN_SPEED + + def __init__(self): + ChassisBase.__init__(self) + + def get_pmc_register(self, reg_name): + # On successful read, returns the value read from given + # reg_name and on failure returns 'ERR' + rv = 'ERR' + mb_reg_file = self.MAILBOX_DIR + '/' + reg_name + + if (not os.path.isfile(mb_reg_file)): + return rv + + try: + with open(mb_reg_file, 'r') as fd: + rv = fd.read() + except Exception as error: + rv = 'ERR' + + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + + def get_reboot_cause(self): + """ + Retrieves the cause of the previous reboot + """ + reset_reason = int(self.get_pmc_register('smf_reset_reason')) + power_reason = int(self.get_pmc_register('smf_poweron_reason')) + + # Reset_Reason = 11 ==> PowerLoss + # So return the reboot reason from Last Power_Reason Dictionary + # If Reset_Reason is not 11 return from Reset_Reason dictionary + # Also check if power_reason, reset_reason are valid values by + # checking key presence in dictionary else return + # REBOOT_CAUSE_HARDWARE_OTHER as the Power_Reason and Reset_Reason + # registers returned invalid data + if (reset_reason == 11): + if (power_reason in self.power_reason_dict): + return (self.power_reason_dict[power_reason], None) + else: + if (reset_reason in self.reset_reason_dict): + return (self.reset_reason_dict[reset_reason], None) + + return (ChassisBase.REBOOT_CAUSE_HARDWARE_OTHER, "Invalid Reason") +