DellEMC: S5296F Platform API 2.0 changes (#11162)

Why I did it
S5296F - Platform API 2.0 changes

How I did it
Implemented the functional API's needed for Platform API 2.0

How to verify it
Used the API 2.0 test suite to validate the test cases.
This commit is contained in:
arunlk-dell 2022-08-26 05:37:23 +05:30 committed by GitHub
parent 8d06de37ae
commit 13bd63e73a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 3052 additions and 336 deletions

View File

@ -0,0 +1,578 @@
{
"chassis": {
"name": "S5296F-ON",
"status_led": {
"controllable": true,
"colors": ["green", "flashing green", "yellow", "flashing yellow"]
},
"thermal_manager" : false,
"components": [
{
"name": "BIOS"
},
{
"name": "FPGA"
},
{
"name": "BMC"
},
{
"name": "System CPLD"
},
{
"name": "Secondary CPLD 1"
},
{
"name": "Secondary CPLD 2"
},
{
"name": "Secondary CPLD 3"
},
{
"name": "Secondary CPLD 4"
}
],
"fans": [
{
"name": "FanTray1-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
},
{
"name": "FanTray2-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
},
{
"name": "FanTray3-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
},
{
"name": "FanTray4-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
],
"fan_drawers":[
{
"name": "FanTray1",
"status_led": {
"controllable": false
},
"fans": [
{
"name": "FanTray1-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
]
},
{
"name": "FanTray2",
"status_led": {
"controllable": false
},
"fans": [
{
"name": "FanTray2-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
]
},
{
"name": "FanTray3",
"status_led": {
"controllable": false
},
"fans": [
{
"name": "FanTray3-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
]
},
{
"name": "FanTray4",
"status_led": {
"controllable": false
},
"fans": [
{
"name": "FanTray4-Fan1",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
]
}
],
"psus": [
{
"name": "PSU1",
"status_led": {
"controllable": false
},
"fans": [
{
"name": "PSU1 Fan",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
]
},
{
"name": "PSU2",
"status_led": {
"controllable": false
},
"fans": [
{
"name": "PSU2 Fan",
"speed": {
"controllable": false
},
"status_led": {
"available": false
}
}
]
}
],
"thermals": [
{
"name": "ASIC On-board",
"controllable": false,
"low-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "CPU On-board",
"controllable": false,
"low-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "Inlet Airflow Sensor",
"controllable": false,
"low-crit-threshold": false,
"high-threshold": false,
"high-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PSU1 Airflow Sensor",
"controllable": false,
"low-crit-threshold": false,
"high-threshold": false,
"high-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PSU1 Sensor",
"controllable": false,
"low-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PSU2 Airflow Sensor",
"controllable": false,
"low-crit-threshold": false,
"high-threshold": false,
"high-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PSU2 Sensor",
"controllable": false,
"low-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PT Left Sensor",
"controllable": false,
"low-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PT Middle Sensor",
"controllable": false,
"low-crit-threshold": false,
"high-threshold": false,
"high-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
},
{
"name": "PT Right Sensor",
"controllable": false,
"low-crit-threshold": false,
"high-threshold": false,
"high-crit-threshold": false,
"minimum-recorded": false,
"maximum-recorded": false
}
],
"modules": [],
"sfps": [
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "SFP/SFP+/SFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
},
{
"name": "QSFP28"
}
]
},
"interfaces": {}
}

View File

@ -1,8 +1,3 @@
{
"skip_sensors": true,
"skip_fancontrol": true,
"skip_ledd": true,
"skip_psud": true,
"skip_syseepromd": true,
"skip_thermalctld": true
"skip_ledd": true
}

View File

@ -4,6 +4,7 @@ s5296f/scripts/sensors usr/bin
s5296f/scripts/pcisysfs.py usr/bin
s5296f/cfg/s5296f-modules.conf etc/modules-load.d
s5296f/systemd/platform-modules-s5296f.service etc/systemd/system
s5296f/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-dellemc_s5296f_c3538-r0
common/platform_reboot usr/share/sonic/device/x86_64-dellemc_s5296f_c3538-r0
common/fw-updater usr/local/bin
common/onie_mode_set usr/local/bin

View File

@ -55,6 +55,11 @@ override_dh_auto_build:
cd $(MOD_SRC_DIR)/$${mod}; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
elif [ $$mod = "s5296f" ]; then \
cp $(COMMON_DIR)/ipmihelper.py $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
cd $(MOD_SRC_DIR)/$${mod}; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd $(MOD_SRC_DIR); \
elif [ $$mod = "n3248te" ]; then \
cd $(MOD_SRC_DIR)/$${mod}; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
@ -143,6 +148,11 @@ 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 = "s5296f" ]; 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 = "z9332f" ]; then \
rm -f $(MOD_SRC_DIR)/$${mod}/sonic_platform/ipmihelper.py; \
rm -f $(MOD_SRC_DIR)/$${mod}/modules/*.whl; \

View File

@ -89,7 +89,7 @@ struct fpgapci_dev {
unsigned int irq_first;
unsigned int irq_length;
unsigned int irq_assigned;
unsigned int xcvr_intr_count;
};
static int use_irq = 1;
@ -107,6 +107,13 @@ MODULE_PARM_DESC(num_bus,
/* Subsystem: Xilinx Corporation Device 0007 */
//#define VENDOR 0x10EE
#define DEVICE 0x7021
/* Altera FPGA PCIE info:
Unassigned class [ff00]: Altera Corporation Device 0004 (rev 01)
Subsystem: Altera Corporation Device 0004 */
#define PCI_VENDOR_ID_ALTERA 0x1172
#define PCI_DEVICE_ID_ALTERA 0x0004
static phys_addr_t fpga_phys_addr;
typedef signed char s8;
@ -190,7 +197,7 @@ struct fpgalogic_i2c {
#define FPGAI2C_REG_STAT_NACK 0x80
/* SR[7:0] - Status register */
#define FPGAI2C_REG_SR_RXACK (1 << 7) /* Receive acknowledge from slave 1 = No acknowledge received*/
#define FPGAI2C_REG_SR_RXACK (1 << 7) /* Receive acknowledge from secondary <20>1<EFBFBD> = No acknowledge received*/
#define FPGAI2C_REG_SR_BUSY (1 << 6) /* Busy, I2C bus busy (as defined by start / stop bits) */
#define FPGAI2C_REG_SR_AL (1 << 5) /* Arbitration lost - fpga i2c logic lost arbitration */
#define FPGAI2C_REG_SR_TIP (1 << 1) /* Transfer in progress */
@ -227,13 +234,6 @@ enum {
#define I2C_PCI_BUS_NUM_12 12
#define I2C_PCI_BUS_NUM_16 16
#define IRQ_LTCH_STS 0x20
#define PRSNT_LTCH_STS 0x10
#define PORT_CTRL_OFFSET 0x4000
#define PORT_STS_OFFSET 0x4004
#define PORT_IRQ_STS_OFFSET 0x4008
#define PORT_IRQ_EN_OFFSET 0x400C
#define MB_BRD_REV_TYPE 0x0008
#define MB_BRD_REV_MASK 0x00f0
#define MB_BRD_REV_00 0x0000
@ -256,7 +256,7 @@ enum {
#define BRD_TYPE_S5232_NON_NEBS 0xc
#define BRD_TYPE_S5232_NEBS 0xd
#define FPGA_CTL_REG_SIZE 0x6000
#define FPGA_CTL_REG_SIZE 0x60
#define MSI_VECTOR_MAP_MASK 0x1f
#define MSI_VECTOR_MAP1 0x58
#define I2C_CH1_MSI_MAP_VECT_8 0x00000008
@ -292,8 +292,6 @@ enum {
#define MSI_VECTOR_REV_00 16
#define MSI_VECTOR_REV_01 32
#define FPGA_MSI_VECTOR_ID_4 4
#define FPGA_MSI_VECTOR_ID_5 5
#define FPGA_MSI_VECTOR_ID_8 8
#define FPGA_MSI_VECTOR_ID_9 9
#define FPGA_MSI_VECTOR_ID_10 10
@ -312,7 +310,7 @@ enum {
#define FPGA_MSI_VECTOR_ID_23 23
#define FPGA_MSI_VECTOR_ID_24 24
#define MAX_WAIT_LOOP 10
static int total_i2c_pci_bus = 0;
static uint32_t board_rev_type = 0;
@ -431,7 +429,7 @@ static int fpgai2c_poll(struct fpgalogic_i2c *i2c)
}
/* Error? */
if (stat & FPGAI2C_REG_STAT_ARBLOST) {
if ((stat & FPGAI2C_REG_STAT_ARBLOST) || ( i2c->msg == NULL) || ( i2c->msg->buf == NULL)) {
i2c->state = STATE_ERROR;
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_STOP);
return -EAGAIN;
@ -516,72 +514,6 @@ static int fpgai2c_poll(struct fpgalogic_i2c *i2c)
return 0;
}
static ssize_t get_mod_msi(struct device *dev, struct device_attribute *devattr, char *buf)
{
int ind = 0, port_status=0, port_irq_status=0;
struct fpgapci_dev *fpgapci = (struct fpgapci_dev*) dev_get_drvdata(dev);
PRINT("%s:xcvr_intr_count:%u\n", __FUNCTION__, fpgapci->xcvr_intr_count);
for(ind=0;ind<64;ind++)
{
port_status = ioread32(fpga_ctl_addr + PORT_STS_OFFSET + (ind*16));
port_irq_status = ioread32(fpga_ctl_addr + PORT_IRQ_STS_OFFSET + (ind*16));
PRINT("%s:port:%d, port_status:%#x, port_irq_status:%#x\n", __FUNCTION__, ind, port_status, port_irq_status);
}
return sprintf(buf,"0x%04x\n",fpgapci->xcvr_intr_count);
}
static DEVICE_ATTR(port_msi, S_IRUGO, get_mod_msi, NULL);
static struct attribute *port_attrs[] = {
&dev_attr_port_msi.attr,
NULL,
};
static struct attribute_group port_attr_grp = {
.attrs = port_attrs,
};
static irqreturn_t fpgaport_1_32_isr(int irq, void *dev)
{
struct pci_dev *pdev = dev;
struct fpgapci_dev *fpgapci = (struct fpgapci_dev*) dev_get_drvdata(&pdev->dev);
int ind = 0, port_status=0, port_irq_status=0;
for(ind=0;ind<32;ind++)
{
port_irq_status = ioread32(fpga_ctl_addr + PORT_IRQ_STS_OFFSET + (ind*16));
if(port_irq_status&(IRQ_LTCH_STS|PRSNT_LTCH_STS))
{
PRINT("%s:port:%d, port_status:%#x, port_irq_status:%#x\n", __FUNCTION__, ind, port_status, port_irq_status);
//write on clear
iowrite32( IRQ_LTCH_STS|PRSNT_LTCH_STS,fpga_ctl_addr + PORT_IRQ_STS_OFFSET + (ind*16));
}
}
fpgapci->xcvr_intr_count++;
PRINT("%s: xcvr_intr_count:%u\n", __FUNCTION__, fpgapci->xcvr_intr_count);
sysfs_notify(&pdev->dev.kobj, NULL, "port_msi");
return IRQ_HANDLED;
}
static irqreturn_t fpgaport_33_64_isr(int irq, void *dev)
{
struct pci_dev *pdev = dev;
struct fpgapci_dev *fpgapci = (struct fpgapci_dev*) dev_get_drvdata(&pdev->dev);
int ind = 0, port_status=0, port_irq_status=0;
for(ind=32;ind<64;ind++)
{
port_irq_status = ioread32(fpga_ctl_addr + PORT_IRQ_STS_OFFSET + (ind*16));
if(port_irq_status| (IRQ_LTCH_STS|PRSNT_LTCH_STS))
{
PRINT("%s:port:%d, port_status:%#x, port_irq_status:%#x\n", __FUNCTION__, ind, port_status, port_irq_status);
iowrite32( IRQ_LTCH_STS|PRSNT_LTCH_STS,fpga_ctl_addr + PORT_IRQ_STS_OFFSET + (ind*16));
}
}
fpgapci->xcvr_intr_count++;
PRINT("%s: xcvr_intr_count:%u\n", __FUNCTION__, fpgapci->xcvr_intr_count);
sysfs_notify(&pdev->dev.kobj, NULL, "port_msi");
return IRQ_HANDLED;
}
static void fpgai2c_process(struct fpgalogic_i2c *i2c)
{
struct i2c_msg *msg = i2c->msg;
@ -610,6 +542,12 @@ static void fpgai2c_process(struct fpgalogic_i2c *i2c)
return;
}
/* Spurious IRQs would lead to invocation of handler with msg being NULL.
* Skip handling them.
*/
if (msg == NULL)
return;
if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {
i2c->state =
(msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE;
@ -621,6 +559,11 @@ static void fpgai2c_process(struct fpgalogic_i2c *i2c)
}
} else
{
if(( i2c->msg == NULL) || ( i2c->msg->buf == NULL) || (i2c->pos >= i2c->msg->len) ) {
printk("crash debug..1 fpgai2c_process MSG and MAS->BUFF is NULL or pos > len ");
return;
}
msg->buf[i2c->pos++] = fpgai2c_reg_get(i2c, FPGAI2C_REG_DATA);
}
@ -657,12 +600,25 @@ static void fpgai2c_process(struct fpgalogic_i2c *i2c)
}
if (i2c->state == STATE_READ) {
if(( i2c->msg == NULL) || ( i2c->msg->buf == NULL) || (i2c->pos >= i2c->msg->len) ) {
printk("crash debug..2 fpgai2c_process MSG and MAS->BUFF is NULL or pos > len ");
return;
}
PRINT("fpgai2c_poll STATE_READ i2c->pos=%d msg->len-1 = 0x%x set FPGAI2C_REG_CMD = 0x%x\n",i2c->pos, msg->len-1,
i2c->pos == (msg->len-1) ? FPGAI2C_REG_CMD_READ_NACK : FPGAI2C_REG_CMD_READ_ACK);
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, i2c->pos == (msg->len-1) ?
FPGAI2C_REG_CMD_READ_NACK : FPGAI2C_REG_CMD_READ_ACK);
} else {
PRINT("fpgai2c_process set FPGAI2C_REG_DATA(0x%x)\n",FPGAI2C_REG_DATA);
if(( i2c->msg == NULL) || ( i2c->msg->buf == NULL) || (i2c->pos >= i2c->msg->len) ) {
printk("crash debug..3 fpgai2c_process MSG and MAS->BUFF is NULL or pos > len ");
return;
}
fpgai2c_reg_set(i2c, FPGAI2C_REG_DATA, msg->buf[i2c->pos++]);
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_WRITE);
}
@ -745,7 +701,7 @@ static int fpgai2c_init(struct fpgalogic_i2c *i2c)
{
int prescale;
int diff;
u8 ctrl;
u8 ctrl = 0, stat, loop = 0;
if (i2c->reg_io_width == 0)
i2c->reg_io_width = 1; /* Set to default value */
@ -776,8 +732,6 @@ static int fpgai2c_init(struct fpgalogic_i2c *i2c)
}
}
ctrl = fpgai2c_reg_get(i2c, FPGAI2C_REG_CONTROL);
PRINT("%s(), line:%d\n", __func__, __LINE__);
PRINT("i2c->base = 0x%p\n",i2c->base);
@ -806,12 +760,23 @@ static int fpgai2c_init(struct fpgalogic_i2c *i2c)
fpgai2c_reg_set(i2c, FPGAI2C_REG_PREHIGH, prescale >> 8);
/* Init the device */
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_IACK);
if (!use_irq)
fpgai2c_reg_set(i2c, FPGAI2C_REG_CONTROL, ctrl | FPGAI2C_REG_CTRL_EN);
else
fpgai2c_reg_set(i2c, FPGAI2C_REG_CONTROL, ctrl | FPGAI2C_REG_CTRL_IEN | FPGAI2C_REG_CTRL_EN);
if (use_irq) {
/* Clear any pending interrupts */
fpgai2c_reg_set(i2c, FPGAI2C_REG_CMD, FPGAI2C_REG_CMD_IACK);
while (loop < MAX_WAIT_LOOP) {
stat = fpgai2c_reg_get(i2c, FPGAI2C_REG_STATUS);
if (stat & FPGAI2C_REG_STAT_IF) {
udelay(100);
loop++;
} else {
break;
}
}
if (loop >=10) {
printk("interrupts can't be cleared: loop %d\n", loop);
}
}
fpgai2c_dump(i2c);
/* Initialize interrupt handlers if not already done */
@ -820,6 +785,17 @@ static int fpgai2c_init(struct fpgalogic_i2c *i2c)
return 0;
}
static int fpgai2c_interrupt_enable(struct fpgapci_dev *fpgapci)
{
int i;
u8 ctrl = 0;
/* Enable Interrupts */
for (i = 0 ; i < total_i2c_pci_bus; i ++) {
fpgai2c_reg_set(&fpgalogic_i2c[i], FPGAI2C_REG_CONTROL, ctrl | FPGAI2C_REG_CTRL_IEN | FPGAI2C_REG_CTRL_EN);
}
return 0;
}
static u32 fpgai2c_func(struct i2c_adapter *adap)
{
@ -861,56 +837,17 @@ static int i2c_init_internal_data(void)
}
static int i2c_pci_init (void)
static int i2c_pci_init (struct fpgapci_dev *fpgapci)
{
int i;
if (num_bus == 0) {
board_rev_type = ioread32(fpga_ctl_addr + MB_BRD_REV_TYPE);
if ((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_00) {
num_bus = I2C_PCI_MAX_BUS_REV00;
} else if (((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_01) ||
((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_02) ||
((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_03)) {
switch (board_rev_type & MB_BRD_TYPE_MASK){
case BRD_TYPE_S5212_NON_NEBS:
case BRD_TYPE_S5212_NEBS:
num_bus = I2C_PCI_BUS_NUM_5;
break;
case BRD_TYPE_S5224_NON_NEBS:
case BRD_TYPE_S5224_NEBS:
num_bus = I2C_PCI_BUS_NUM_7;
break;
case BRD_TYPE_Z9232_NON_NEBS:
case BRD_TYPE_Z9232_NEBS:
case BRD_TYPE_S5232_NON_NEBS:
case BRD_TYPE_S5232_NEBS:
num_bus = I2C_PCI_BUS_NUM_8;
break;
case BRD_TYPE_S5248_NON_NEBS:
case BRD_TYPE_S5248_NEBS:
if ((fpgapci != NULL) && (fpgapci->pci_dev->vendor == PCI_VENDOR_ID_ALTERA)) {
num_bus = I2C_PCI_BUS_NUM_10;
break;
case BRD_TYPE_Z9264_NON_NEBS:
case BRD_TYPE_Z9264_NEBS:
num_bus = I2C_PCI_BUS_NUM_12;
break;
case BRD_TYPE_S5296_NON_NEBS:
case BRD_TYPE_S5296_NEBS:
num_bus = I2C_PCI_BUS_NUM_16;
break;
default:
num_bus = I2C_PCI_BUS_NUM_16;
printk("Wrong BRD_TYPE: 0x%x\n", board_rev_type);
break;
}
} else {
printk("Wrong board_rev_type 0x%x\n", board_rev_type);
}
num_bus = I2C_PCI_MAX_BUS;
}
printk("board_rev_type 0x%x, num_bus 0x%x\n", board_rev_type, num_bus);
printk("vendor 0x%x, num_bus 0x%x\n", fpgapci->pci_dev->vendor, num_bus);
total_i2c_pci_bus = num_bus;
memset (&i2c_pci_adap, 0, sizeof(i2c_pci_adap));
@ -1100,84 +1037,8 @@ static int register_intr_handler(struct pci_dev *dev, int irq_num_id)
PRINT ( ": fpgapci_dev is 0\n");
return err;
}
if ((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_00) {
/* Request interrupt line for unique function
* alternatively function will be called from free_irq as well
* with flag IRQF_SHARED */
switch(irq_num_id) {
/* Currently we only support test vector 2 for FPGA Logic I2C channel
* controller 1-7 interrupt*/
case FPGA_MSI_VECTOR_ID_4:
err = request_irq(dev->irq + irq_num_id, fpgaport_1_32_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, dev);
PRINT ( "%d: fpgapci_dev: irq: %d, %d\n", __LINE__, dev->irq, irq_num_id);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_5:
err = request_irq(dev->irq + irq_num_id, fpgaport_33_64_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, dev);
PRINT ( "%d: fpgapci_dev: irq: %d, %d\n", __LINE__, dev->irq, irq_num_id);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_8:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[0]);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_9:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[1]);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_10:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[2]);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_11:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[3]);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_12:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[4]);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_13:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[5]);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_14:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[6]);
fpgapci->irq_assigned++;
break;
default:
PRINT("No more interrupt handler for number (%d)\n",
dev->irq + irq_num_id);
break;
}
} else if (((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_01) ||
((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_02) ||
((board_rev_type & MB_BRD_REV_MASK) == MB_BRD_REV_03)) {
/* FPGA SPEC 4.3.1.34, First i2c channel mapped to vector 8 */
switch (irq_num_id) {
case FPGA_MSI_VECTOR_ID_4:
err = request_irq(dev->irq + irq_num_id, fpgaport_1_32_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, dev);
PRINT ( "%d: fpgapci_dev: irq: %d, %d\n", __LINE__, dev->irq, irq_num_id);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_5:
err = request_irq(dev->irq + irq_num_id, fpgaport_33_64_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, dev);
PRINT ( "%d: fpgapci_dev: irq: %d, %d\n", __LINE__, dev->irq, irq_num_id);
fpgapci->irq_assigned++;
break;
case FPGA_MSI_VECTOR_ID_8:
err = request_irq(dev->irq + irq_num_id, fpgai2c_isr, IRQF_EARLY_RESUME,
FPGA_PCI_NAME, &fpgalogic_i2c[0]);
@ -1289,7 +1150,6 @@ static int register_intr_handler(struct pci_dev *dev, int irq_num_id)
dev->irq + irq_num_id);
break;
}
}
return err;
}
@ -1415,7 +1275,7 @@ static int fpgapci_setup_device(struct fpgapci_dev *fpgapci,struct pci_dev *dev)
goto fail_map_bars;
}
i2c_pci_init();
i2c_pci_init(fpgapci);
return 0;
/* ERROR HANDLING */
@ -1491,7 +1351,6 @@ error_no_msi:
static int fpgapci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct fpgapci_dev *fpgapci = 0;
int status = 0;
#ifdef TEST
PRINT ( " vendor = 0x%x, device = 0x%x, class = 0x%x, bus:slot.func = %02x:%02x.%02x\n",
@ -1508,11 +1367,6 @@ static int fpgapci_probe(struct pci_dev *dev, const struct pci_device_id *id)
fpgapci->pci_dev = dev;
dev_set_drvdata(&dev->dev, (void*)fpgapci);
status = sysfs_create_group(&dev->dev.kobj, &port_attr_grp);
if (status) {
printk(KERN_INFO "%s:Cannot create sysfs\n", __FUNCTION__);
}
fpgapci->upstream = find_upstream_dev (dev);
if(fpgapci_setup_device(fpgapci,dev)) {
@ -1523,6 +1377,9 @@ static int fpgapci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if(fpgapci_configure_msi(fpgapci,dev)) {
goto error_cannot_configure;
}
/* Enable interrupt after config msi */
fpgai2c_interrupt_enable(fpgapci);
}
@ -1581,6 +1438,7 @@ static void fpgapci_remove(struct pci_dev *dev)
static const struct pci_device_id fpgapci_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_XILINX, DEVICE)},
{PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_ALTERA)},
{0, },
};
@ -1624,3 +1482,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("joyce_yu@dell.com");
MODULE_DESCRIPTION ("Driver for FPGA Logic I2C bus");
MODULE_SUPPORTED_DEVICE ("FPGA Logic I2C bus");
MODULE_VERSION ("01.01");

View File

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

View File

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

View File

@ -0,0 +1,453 @@
#!/usr/bin/env python
#############################################################################
# DELLEMC S5296F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
import os
import time
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_drawer import FanDrawer
from sonic_platform.hwaccess import pci_get_value, pci_set_value
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
MAX_S5296F_COMPONENT = 8 # BIOS,BMC,FPGA,SYSTEM CPLD,4 SECONDARY CPLDs
MAX_S5296F_FANTRAY = 4
MAX_S5296F_FAN = 1
MAX_S5296F_PSU = 2
MAX_S5296F_THERMAL = 10
SYSTEM_LED_REG = 0x24
SYSTEM_BEACON_LED_SET = 0x8
SYSTEM_BEACON_LED_CLEAR = 0xFFFFFFF7
media_part_num_list = set([ \
"8T47V","XTY28","MHVPK","GF76J","J6FGD","F1KMV","9DN5J","H4DHD","6MCNV","0WRX0","X7F70","5R2PT","WTRD1","WTRD1","WTRD1","WTRD1","5250G","WTRD1","C5RNH","C5RNH","FTLX8571D3BCL-FC",
"C5RNH","5250G","N8TDR","7D64H","7D64H","RN84N","RN84N","HMTNW","6K3Y6","6K3Y6","TY5FM","50M0R","PGYJT","WP2PP","85Y13","1HCGH","FP9R1","FYD0M","C6Y7M","C6Y7M","V250M","V250M",
"5CWK6","5CWK6","53HVN","53HVN","358VV","358VV","MV799","MV799","YJF03","P9GND","T1KCN","1DXKP","MT7R2","K0T7R","W5G04","7TCDN","7TCDN","7TCDN","7TCDN","7TCDN","V3XJK","0MV31",
"5FVP7","N6KM9","C41MF","77KC3","XW7J0","V4NJV","2XJHY","H93DH","H93DH","F8CG0","F8CG0","F8CG0","119N6","WFMF5","794RX","288F6","1M31V","1M31V","5NP8R","5NP8R","4TC09","4TC09",
"FC6KV","FC6KV","J90VN","J90VN","05RH0","05RH0","YDN52","0C2YV","YDN52","0C2YV","9JT65","D7M6H","6GW14","FYVFW","0VF5H","P4YPY","P4YPY","TCPM2","TCPM2","JNPF8","JNPF8","27GG5",
"27GG5","P8T4W","P8T4W","JR54Y","M6N0J","XJYD0","K44H9","035KG","P7C7N","76V43","3CC35","FN4FC","26FN3","YFNDD","YFNDD","7R9N9","035KG","P7C7N","76V43","3CC35","PLRXPLSCS43811",
"FN4FC","26FN3","YFNDD","YFNDD","7R9N9","G86YJ","V407F","V407F","9KH6T","G86YJ","V407F","9KH6T","2JVDD","D0R73","VXFJY","9X8JP","2JVDD","D0R73","VXFJY","9X8JP","2JVDD","D0R73","VXFJY",
"9X8JP","GMFC5","GMFC5","GMFC5","D7P80","3MFXG","3MFXG","0GWXJ","THPF3","THPF3","THPF3","THPF3","THPF3","PJ62G","3XCX1","JJYKG","RRRTK","16K56","86JM2","K5R6C","7MG2C","WTPPN","9HTT2",
"NKM4F","VXGGG","JC9W6","6MR8M","RP3GV","M5PPJ","XKY55","TKCXT","05J8P","5WGKD","XFDRT","NW8DM","YPKH3","5WGKD","XFDRT","NW8DM","YPKH3","71XXK","MVCX6","0XYP6","HPPVW","3GHRT","71XXK",
"MVCX6","0XYP6","HPPVW","3GHRT","2X5T6","135V2","KD5MV","2X5T6","KD5MV","HHFK0","3YWG7","5CMT2","RCVP5","X5DH4","HHFK0","3YWG7","5CMT2","RCVP5","X5DH4","3YWG7","5CMT2","RCVP5","X5DH4",
"4WJ41","4WJ41","14NV5","14NV5","14NV5","4WGYD","YKMH7","X7CCC","X7CCC","0X9CT","0CY8V","P7D7R","W4GPP","W4GPP","W4GPP","HHHCHC","07RN7","07RN7","0YR96","0YR96","JCYM9","FTLX8571D3BCL",
"DDW0X","VPFDJ","229KM","9FC7D","DDW0X","VPFDJ","6FMR5","J7K20","N3K9W","6FMR5","8R4VM","7VN5T","D9YM8","8R4VM","VYXPW","87TPX","WY6FK","VYXPW","87TPX","WY6FK","WG8C4","N8K82","2DV6Y",
"77C3C","RC0HM","77C3C","RC0HM","JHXTN","3P3PG","92YVM","4VX5M","4VX5M","6RRGD","W4JWV","22V6R","XR11M","9GMDY","JMCWK","TP2F0","6MGDY","78RHK", "C0TP5","0WDNV","FCLF8522P2BTL"\
])
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"
PCI_RES = "/sys/bus/pci/devices/0000:04:00.0/resource0"
oir_fd = -1
sysled_offset = 0x0024
SYSLED_COLOR_TO_REG = {
"blinking_green": 0x0,
"green" : 0x10,
"amber" : 0x20,
"blinking_amber": 0x30
}
REG_TO_SYSLED_COLOR = {
0x0 : "blinking_green",
0x10 : "green",
0x20 : "amber",
0x30 : "blinking_amber"
}
_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 = 104
self.SFP28_PORT_END = 96
i2c_bus_for_port = 2
i2c_mux_to_populate = 603
i2c_mux_is_good = False
PORTS_IN_BLOCK = (self.PORT_END + 1)
_sfp_port = range(1, self.SFP28_PORT_END + 1)
eeprom_base = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/eeprom"
mux_channel = "/sys/class/i2c-adapter/i2c-{0}/{0}-0074/channel-0"
for index in range(self.PORT_START, PORTS_IN_BLOCK):
eeprom_path = ""
if index%8 == 1: # 8 buses per i2c mux
i2c_mux_is_good = True if os.path.exists(mux_channel.format(i2c_mux_to_populate)) else False
i2c_mux_to_populate += 1
if i2c_mux_is_good:
eeprom_path = eeprom_base.format(i2c_bus_for_port)
i2c_bus_for_port += 1
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._num_sfps = len(self._sfp_list)
self._eeprom = Eeprom()
for i in range(MAX_S5296F_THERMAL):
self._thermal_list.append(Thermal(i))
for i in range(MAX_S5296F_COMPONENT):
self._component_list.append(Component(i))
for i in range(MAX_S5296F_PSU):
self._psu_list.append(Psu(i))
for i in range(MAX_S5296F_FANTRAY):
fandrawer = FanDrawer(i)
self._fan_drawer_list.append(fandrawer)
self._fan_list.extend(fandrawer._fan_list)
self._num_fans = MAX_S5296F_FANTRAY * MAX_S5296F_FAN
self._watchdog = Watchdog()
def __del__(self):
if self.oir_fd != -1:
self.epoll.unregister(self.oir_fd.fileno())
self.epoll.close()
self.oir_fd.close()
# not needed /delete after validation
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 ex:
pass
retval = retval.rstrip('\r\n')
retval = retval.lstrip(" ")
return retval
# not needed /delete after validation
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
# check for this event change for sfp / do we need to handle timeout/sleep
def get_change_event(self, timeout=0):
"""
Returns a nested dictionary containing all devices which have
experienced a change at chassis level
"""
start_ms = time.time() * 1000
port_dict = {}
change_dict = {}
change_dict['sfp'] = port_dict
if not self._global_port_pres_dict:
for port_num in self._sfp_port:
presence = self.get_sfp(port_num).get_presence()
self._global_port_pres_dict[port_num] = '1' if presence else '0'
while True:
time.sleep(0.5)
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:
now_ms = time.time() * 1000
if (now_ms - start_ms >= timeout):
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' is 1-based
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 set_status_led(self, color):
"""
Sets the state of the system LED
Args:
color: A string representing the color with which to set the
system LED
Returns:
bool: True if system LED state is set successfully, False if not
"""
if color not in list(self.SYSLED_COLOR_TO_REG.keys()):
return False
val = pci_get_value(self.PCI_RES, self.sysled_offset)
val = (val & 0xFFCF) | self.SYSLED_COLOR_TO_REG[color]
pci_set_value(self.PCI_RES, val, self.sysled_offset)
self.sys_ledcolor = color
return True
def get_status_led(self):
"""
Gets the state of the system LED
Returns:
A string, one of the valid LED color strings which could be
vendor specified.
"""
val = pci_get_value(self.PCI_RES, self.sysled_offset)
if val != -1:
val = val & 0x30
return self.REG_TO_SYSLED_COLOR.get(val)
return self.sys_ledcolor
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
def get_revision(self):
"""
Retrieves the revision number of the chassis (Service tag)
Returns:
string: Revision number of chassis
"""
return self._eeprom.revision_str()
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 ex:
return (self.REBOOT_CAUSE_NON_HARDWARE, None)
if reboot_cause & 0x1:
return (self.REBOOT_CAUSE_POWER_LOSS, "Power on reset")
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)
def get_qualified_media_list(self):
return media_part_num_list
def set_locator_led(self, color):
"""
Sets the state of the Chassis Locator LED
Args:
color: A string representing the color with which to set the Chassis Locator LED
Returns:
bool: True if the Chassis Locator LED state is set successfully, False if not
"""
val = pci_get_value(self.PCI_RES, SYSTEM_LED_REG)
if self.LOCATOR_LED_ON == color:
val = int(val) | SYSTEM_BEACON_LED_SET
elif self.LOCATOR_LED_OFF == color:
val = int(val) & SYSTEM_BEACON_LED_CLEAR
else:
return False
pci_set_value(self.PCI_RES, val, SYSTEM_LED_REG)
return True
def get_locator_led(self):
"""
Gets the state of the Chassis Locator LED
Returns:
LOCATOR_LED_ON or LOCATOR_LED_OFF
"""
val = pci_get_value(self.PCI_RES, SYSTEM_LED_REG)
val = int(val) & SYSTEM_BEACON_LED_SET
if not val:
return self.LOCATOR_LED_OFF
else:
return self.LOCATOR_LED_ON
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return -1
def is_replaceable(self):
"""
Indicate whether Chassis is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False

View File

@ -0,0 +1,233 @@
#!/usr/bin/env python
########################################################################
# DELLEMC S5296F
#
# 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']).decode('utf-8').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():
""" Returns BMC Version """
bmc_ver = ''
try:
bmc_ver = subprocess.check_output(
"ipmitool mc info | awk '/Firmware Revision/ { print $NF }'",
shell=True, text=True).strip()
except (FileNotFoundError, subprocess.CalledProcessError):
pass
return bmc_ver
def get_cpld_version(bus, i2caddr):
""" Return CPLD version """
major = hwaccess.i2c_get(bus, i2caddr, 1)
minor = hwaccess.i2c_get(bus, i2caddr, 0)
if major != -1 and minor != -1:
return '{}.{}'.format(major, minor)
return ''
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)
def get_cpld3_version():
return get_cpld_version(600, 0x32)
def get_cpld4_version():
return get_cpld_version(600, 0x33)
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
],
['Secondary CPLD 1',
'Used for managing SFP28/QSFP28 port transceivers (SFP28 1-24, QSFP28 1-4)',
get_cpld1_version
],
['Secondary CPLD 2',
'Used for managing SFP28/QSFP28 port transceivers (SFP28 25-48, QSFP28 5-8)',
get_cpld2_version
],
['Secondary CPLD 3',
'Used for managing SFP28/QSFP28 port transceivers (SFP28 49-72)',
get_cpld3_version
],
['Secondary CPLD 4',
'Used for managing SFP28/QSFP28 port transceivers (SFP28 73-96)',
get_cpld4_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 get_presence(self):
"""
Retrieves the presence of the component
Returns:
bool: True if present, False if not
"""
return True
def get_model(self):
"""
Retrieves the part number of the component
Returns:
string: Part number of component
"""
return 'NA'
def get_serial(self):
"""
Retrieves the serial number of the component
Returns:
string: Serial number of component
"""
return 'NA'
def get_status(self):
"""
Retrieves the operational status of the component
Returns:
bool: True if component is operating properly, False if not
"""
return True
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return -1
def is_replaceable(self):
"""
Indicate whether component is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
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
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 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

View File

@ -0,0 +1,133 @@
#!/usr/bin/env python
#############################################################################
# DellEmc S5296F
#
# 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
except ImportError as 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 = (eeprom[9] << 8) | 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
+ eeprom[tlv_index + 1]]
code = "0x%02X" % tlv[0]
name, value = self.decoder(None, tlv)
self.eeprom_tlv_dict[code] = value
if eeprom[tlv_index] == self._TLV_CODE_CRC_32:
break
tlv_index += 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].decode('ascii')
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(["{:02x}".format(T) for T in t[2]]).upper()
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].decode('ascii')
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].decode('ascii')
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].decode('ascii')
def revision_str(self):
"""
Returns the device revision
"""
(is_valid, results) = self.get_tlv_field(
self.eeprom_data, self._TLV_CODE_LABEL_REVISION)
if not is_valid:
return "N/A"
return results[2].decode('ascii')
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,214 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5296F
#
# 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
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": 0x53, "State": 0x5b, "Speed": 0x20},
2: {"Prsnt": 0x54, "State": 0x5c, "Speed": 0x21},
3: {"Prsnt": 0x55, "State": 0x5d, "Speed": 0x22},
4: {"Prsnt": 0x56, "State": 0x5e, "Speed": 0x23}
}
PSU_FAN_SENSOR_MAPPING = { 1: {"State": 0x31, "Speed": 0x28},
2: {"State": 0x32, "Speed": 0x29} }
# { 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
self.max_speed_offset = FAN1_MAX_SPEED_OFFSET
self.fan_direction_offset = FAN_DIRECTION_OFFSET
self.index = self.fantrayindex
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 = 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
"""
return self.fru.get_board_part_number()
def get_serial(self):
"""
Retrieves the serial number of the FAN
Returns:
String: Serial number of FAN
"""
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 not state > 1:
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_presence()
if not fan_status:
return None
is_valid, fan_direction = self.fru.get_fru_data(self.fan_direction_offset)
if is_valid and fan_direction[0] < len(direction):
return direction[fan_direction[0]]
else:
return None
def get_speed_rpm(self):
"""
Retrieves the speed of fan in revolutions per minute (RPM)
Returns:
An integer, speed of the fan in RPM
"""
is_valid, fan_speed = self.speed_sensor.get_reading()
return fan_speed if is_valid else None
def get_speed(self):
"""
Retrieves the speed of the fan
Returns:
int: percentage of the max fan speed
"""
rpm = self.get_speed_rpm()
if rpm is None:
return None
if self.max_speed == 0:
is_valid, resp = self.fru.get_fru_data(self.max_speed_offset, 2)
if is_valid:
self.max_speed = (resp[1] << 8) | resp[0]
if self.max_speed == 0:
return None
return (100 * rpm) // self.max_speed
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.fanindex
def is_replaceable(self):
"""
Indicate whether Fan is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False
def get_speed_tolerance(self):
"""
Retrieves the speed tolerance of the fan
Returns:
An integer, the percentage of variance from target speed which is
considered tolerable
"""
if self.get_presence():
# The tolerance value is fixed as 20% for all the DellEMC platforms
tolerance = 20
else:
tolerance = 0
return tolerance
def set_status_led(self, color):
"""
Set led to expected color
Args:
color: A string representing the color with which to set the
fan status LED
Returns:
bool: True if set success, False if fail.
"""
# Fan tray status LED controlled by HW
# Return True to avoid thermalctld alarm
return True

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5296F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the Fan-Drawers' information available in the platform.
#
########################################################################
try:
from sonic_platform_base.fan_drawer_base import FanDrawerBase
from sonic_platform.fan import Fan
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
S5296F_FANS_PER_FANTRAY = 1
class FanDrawer(FanDrawerBase):
"""DellEMC Platform-specific Fan class"""
def __init__(self, fantray_index):
FanDrawerBase.__init__(self)
# FanTray is 1-based in DellEMC platforms
self.fantrayindex = fantray_index + 1
for i in range(S5296F_FANS_PER_FANTRAY):
self._fan_list.append(Fan(fantray_index, i))
def get_name(self):
"""
Retrieves the fan drawer name
Returns:
string: The name of the device
"""
return "FanTray{}".format(self.fantrayindex)
def get_presence(self):
"""
Retrives the presence of the fan drawer
Returns:
bool: True if fan_tray is present, False if not
"""
return self.get_fan(0).get_presence()
def get_model(self):
"""
Retrieves the part number of the fan drawer
Returns:
string: Part number of fan drawer
"""
return "NA"
def get_serial(self):
"""
Retrieves the serial number of the fan drawer
Returns:
string: Serial number of the fan drawer
"""
return "NA"
def get_status(self):
"""
Retrieves the operational status of the fan drawer
Returns:
bool: True if fan drawer is operating properly, False if not
"""
return self.get_fan(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 or -1 if cannot determine the position
"""
return self.fantrayindex
def is_replaceable(self):
"""
Indicate whether this fan drawer is replaceable.
Returns:
bool: True if it is replaceable, False if not
"""
return True
def set_status_led(self, color):
"""
Set led to expected color
Args:
color: A string representing the color with which to set the
fan module status LED
Returns:
bool: True if set success, False if fail.
"""
# Fan tray status LED controlled by BMC
# Return True to avoid thermalctld alarm
return True
def get_maximum_consumed_power(self):
"""
Retrives the maximum power drawn by Fan Drawer
Returns:
A float, with value of the maximum consumable power of the
component.
"""
# Value based on the hw spec
return 54.0

View File

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

View File

@ -0,0 +1,13 @@
# Media settings key plugin
#
# Generate keys used for lookup in media_settings,json
def get_media_settings_key(physical_port, transceiver_dict):
d = transceiver_dict[physical_port]
media_interface = d['media_interface']
generic_key = '{}-{}'.format(d['form_factor'], media_interface)
if media_interface == 'CR':
generic_key = '{}-{}'.format(generic_key, d['cable_length_detailed'])
return ['{}-{}'.format(d['manufacturename'], d['modelname']),
generic_key
]

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,325 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5296F
#
# 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,
"InCurrent": 0x36, "InPower": 0x34,
"InVoltage": 0x35, "Temperature": 0xc },
2: { "State": 0x32, "Current": 0x3F,
"Power": 0x3D, "Voltage": 0x3E,
"InCurrent": 0x3C, "InPower": 0x3A,
"InVoltage": 0x3B, "Temperature": 0xd } }
# ( 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.input_voltage_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["InVoltage"])
self.input_current_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["InCurrent"])
self.input_power_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["InPower"])
self.temp_sensor = IpmiSensor(self.SENSOR_MAPPING[self.index]["Temperature"])
self.fru = IpmiFru(self.FRU_MAPPING[self.index])
self._fan_list.append(Fan(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) == 1:
presence = True
return presence
def get_temperature(self):
"""
Retrieves current temperature reading from thermal
Returns:
A float number of current temperature in Celcius up to
nearest thousandth of one degree celcius, e.g. 30.125
"""
is_valid, temperature = self.temp_sensor.get_reading()
if not is_valid:
temperature = 0
return float(temperature)
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_revision(self):
"""
Retrives thehardware revision of the device
Returns:
String: revision value of device
"""
serial = self.fru.get_board_serial()
if serial != "NA" and len(serial) == 23:
return serial[-3:]
else:
return "NA"
def is_replaceable(self):
"""
Indicate whether this PSU is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True
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:
return None
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:
return None
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:
return None
return float(power)
def get_input_voltage(self):
"""
Retrieves current PSU voltage input
Returns:
A float number, the input voltage in volts,
e.g. 12.1
"""
is_valid, input_voltage = self.input_voltage_sensor.get_reading()
if not is_valid:
return None
return float(input_voltage)
def get_input_current(self):
"""
Retrieves present electric current supplied to PSU
Returns:
A float number, electric current in amperes,
e.g. 15.4
"""
is_valid, input_current = self.input_current_sensor.get_reading()
if not is_valid:
return None
return float(input_current)
def get_input_power(self):
"""
Retrieves current energy supplied to PSU
Returns:
A float number, the power in watts,
e.g. 302.6
"""
is_valid, input_power = self.input_power_sensor.get_reading()
if not is_valid:
return None
return float(input_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
"""
board_product = self.fru.get_board_product()
if board_product is not None :
info = board_product.split(',')
if 'AC' in info : return 'AC'
if 'DC' in info : return 'DC'
return None
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.index
def get_temperature_high_threshold(self):
"""
Returns the high temperature threshold for PSU in Celsius
"""
is_valid, high_threshold = self.temp_sensor.get_threshold("UpperCritical")
if not is_valid:
high_threshold = 105
high_threshold = "{:.2f}".format(high_threshold)
return float(high_threshold)
def get_voltage_low_threshold(self):
"""
Returns PSU low threshold in Volts
"""
is_valid, low_threshold = self.voltage_sensor.get_threshold("LowerCritical")
if not is_valid:
low_threshold = 11.4 #Revisit the value
low_threshold = "{:.2f}".format(low_threshold)
return float(low_threshold)
def get_voltage_high_threshold(self):
"""
Returns PSU high threshold in Volts
"""
is_valid, high_threshold = self.voltage_sensor.get_threshold("UpperCritical")
if not is_valid:
high_threshold = 12.6 #Revisit the value
high_threshold = "{:.2f}".format(high_threshold)
return float(high_threshold)
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
"""
return float(750)
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
"""
# Hardware not supported
return False

View File

@ -0,0 +1,367 @@
#!/usr/bin/env python
#############################################################################
# DELLEMC S5296F
#
# Module contains an implementation of SONiC Platform Base API and
# provides the platform information
#
#############################################################################
try:
import os
import time
import struct
import mmap
from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase
except ImportError as e:
raise ImportError(str(e) + "- required module not found")
QSFP_INFO_OFFSET = 128
SFP_INFO_OFFSET = 0
QSFP_DD_PAGE0 = 0
SFP_TYPE_LIST = [
'0x3' # SFP/SFP+/SFP28 and later
]
QSFP_TYPE_LIST = [
'0xc', # QSFP
'0xd', # QSFP+ or later
'0x11' # QSFP28 or later
]
QSFP_DD_TYPE_LIST = [
'0x18' #QSFP-DD Type
]
class Sfp(SfpOptoeBase):
"""
DELLEMC Platform-specific Sfp class
"""
BASE_RES_PATH = "/sys/bus/pci/devices/0000:04:00.0/resource0"
def __init__(self, index, sfp_type, eeprom_path):
SfpOptoeBase.__init__(self)
self.port_type = sfp_type
self.sfp_type = sfp_type
self.index = index
self.eeprom_path = eeprom_path
self._initialize_media(delay=False)
def get_eeprom_path(self):
return self.eeprom_path
def get_name(self):
return "SFP/SFP+/SFP28" if self.index < 96 else "QSFP28 or later"
def pci_mem_read(self, mm, offset):
mm.seek(offset)
read_data_stream = mm.read(4)
reg_val = struct.unpack('I', read_data_stream)
mem_val = str(reg_val)[1:-2]
# print "reg_val read:%x"%reg_val
return mem_val
def pci_mem_write(self, mm, offset, data):
mm.seek(offset)
# print "data to write:%x"%data
mm.write(struct.pack('I', data))
def pci_set_value(self, resource, val, offset):
fd = os.open(resource, os.O_RDWR)
mm = mmap.mmap(fd, 0)
val = self.pci_mem_write(mm, offset, val)
mm.close()
os.close(fd)
return val
def pci_get_value(self, resource, offset):
fd = os.open(resource, os.O_RDWR)
mm = mmap.mmap(fd, 0)
val = self.pci_mem_read(mm, offset)
mm.close()
os.close(fd)
return val
def _initialize_media(self,delay=False):
"""
Initialize the media type and eeprom driver for SFP
"""
if delay:
time.sleep(1)
self._xcvr_api = None
self.get_xcvr_api()
self.set_media_type()
self.reinit_sfp_driver()
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.index
def is_replaceable(self):
"""
Indicate whether this device is replaceable.
Returns:
bool: True if it is replaceable.
"""
return True
def get_presence(self):
"""
Retrieves the presence of the sfp
Returns : True if sfp is present and false if it is absent
"""
# Check for invalid port_num
mask = {'QSFP' : (1 << 4), 'SFP' : (1 << 0)}
# Port offset starts with 0x4004
port_offset = 16388 + ((self.index-1) * 16)
try:
reg_value = int(self.pci_get_value(self.BASE_RES_PATH, port_offset))
# ModPrsL is active low
if reg_value & mask[self.port_type] == 0:
return True
except ValueError:
pass
return False
def get_reset_status(self):
"""
Retrives the reset status of SFP
"""
reset_status = False
if (self.port_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
reg_value = int(self.pci_get_value(self.BASE_RES_PATH, port_offset))
# Absence of status throws error
if (reg_value == ""):
return reset_status
# Mask off 4th bit for reset status
mask = (1 << 4)
if ((reg_value & mask) == 0):
reset_status = True
else:
reset_status = False
return reset_status
def get_lpmode(self):
"""
Retrieves the lpmode(low power mode) of this SFP
"""
lpmode_state = False
if (self.port_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
reg_value = int(self.pci_get_value(self.BASE_RES_PATH, port_offset))
# Absence of status throws error
if (reg_value == ""):
return lpmode_state
# Mask off 6th bit for lpmode
mask = (1 << 6)
# LPMode is active high
if reg_value & mask == 0:
lpmode_state = False
else:
lpmode_state = True
return lpmode_state
def reset(self):
"""
Reset the SFP and returns all user settings to their default state
"""
if (self.port_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
reg_value = int(self.pci_get_value(self.BASE_RES_PATH, port_offset))
# Absence of status throws error
if (reg_value == ""):
return False
# Mask off 4th bit for reset
mask = (1 << 4)
# ResetL is active low
reg_value = reg_value & ~mask
# Convert our register value back to a hex string and write back
self.pci_set_value(self.BASE_RES_PATH, reg_value, port_offset)
# Sleep 1 second to allow it to settle
time.sleep(1)
reg_value = reg_value | mask
# Convert our register value back to a hex string and write back
self.pci_set_value(self.BASE_RES_PATH, reg_value, port_offset)
return True
else:
return False
def set_lpmode(self, lpmode):
"""
Sets the lpmode(low power mode) of this SFP
"""
if (self.port_type == 'QSFP'):
# Port offset starts with 0x4000
port_offset = 16384 + ((self.index-1) * 16)
reg_value = int(self.pci_get_value(self.BASE_RES_PATH, port_offset))
# Absence of status throws error
if (reg_value == ""):
return False
# Mask off 6th bit for lowpower mode
mask = (1 << 6)
# LPMode is active high; set or clear the bit accordingly
if lpmode is True:
reg_value = reg_value | mask
else:
reg_value = reg_value & ~mask
# Convert our register value back to a hex string and write back
self.pci_set_value(self.BASE_RES_PATH, reg_value, port_offset)
return True
else:
return False
def get_status(self):
"""
Retrieves the operational status of the device
"""
reset = self.get_reset_status()
if (reset == True):
status = False
else:
status = True
return status
def set_media_type(self):
"""
Reads optic eeprom byte to determine media type inserted
"""
eeprom_raw = []
eeprom_raw = self._xcvr_api_factory._get_id()
if eeprom_raw is not None:
eeprom_raw = hex(eeprom_raw)
if eeprom_raw in SFP_TYPE_LIST:
self.sfp_type = 'SFP'
elif eeprom_raw in QSFP_TYPE_LIST:
self.sfp_type = 'QSFP'
elif eeprom_raw in QSFP_DD_TYPE_LIST:
self.sfp_type = 'QSFP_DD'
else:
#Set native port type if EEPROM type is not recognized/readable
self.sfp_type = self.port_type
else:
self.sfp_type = self.port_type
return self.sfp_type
def reinit_sfp_driver(self):
"""
Changes the driver based on media type detected
"""
del_sfp_path = "/sys/class/i2c-adapter/i2c-{0}/delete_device".format(self.index+1)
new_sfp_path = "/sys/class/i2c-adapter/i2c-{0}/new_device".format(self.index+1)
driver_path = "/sys/class/i2c-adapter/i2c-{0}/{0}-0050/name".format(self.index+1)
delete_device = "echo 0x50 >" + del_sfp_path
if not os.path.isfile(driver_path):
print(driver_path, "does not exist")
return False
try:
with os.fdopen(os.open(driver_path, os.O_RDONLY)) as fd:
driver_name = fd.read()
driver_name = driver_name.rstrip('\r\n')
driver_name = driver_name.lstrip(" ")
#Avoid re-initialization of the QSFP/SFP optic on QSFP/SFP port.
if self.sfp_type == 'SFP' and driver_name in ['optoe1', 'optoe3']:
subprocess.Popen(delete_device, shell=True, stdout=subprocess.PIPE)
time.sleep(0.2)
new_device = "echo optoe2 0x50 >" + new_sfp_path
subprocess.Popen(new_device, shell=True, stdout=subprocess.PIPE)
time.sleep(2)
elif self.sfp_type == 'QSFP' and driver_name in ['optoe2', 'optoe3']:
subprocess.Popen(delete_device, shell=True, stdout=subprocess.PIPE)
time.sleep(0.2)
new_device = "echo optoe1 0x50 >" + new_sfp_path
subprocess.Popen(new_device, shell=True, stdout=subprocess.PIPE)
time.sleep(2)
elif self.sfp_type == 'QSFP_DD' and driver_name in ['optoe1', 'optoe2']:
subprocess.Popen(delete_device, shell=True, stdout=subprocess.PIPE)
time.sleep(0.2)
new_device = "echo optoe3 0x50 >" + new_sfp_path
subprocess.Popen(new_device, shell=True, stdout=subprocess.PIPE)
time.sleep(2)
except IOError as e:
print("Error: Unable to open file: %s" % str(e))
return False
def get_max_port_power(self):
"""
Retrieves the maximumum power allowed on the port in watts
"""
return 5.0 if self.sfp_type == 'QSFP' else 2.5
def get_error_description(self):
"""
Retrives the error descriptions of the SFP module
Returns:
String that represents the current error descriptions of vendor specific errors
In case there are multiple errors, they should be joined by '|',
like: "Bad EEPROM|Unsupported cable"
"""
if not self.get_presence():
return self.SFP_STATUS_UNPLUGGED
else:
if not os.path.isfile(self.eeprom_path):
return "EEPROM driver is not attached"
if self.sfp_type == 'SFP':
offset = SFP_INFO_OFFSET
elif self.sfp_type == 'QSFP':
offset = QSFP_INFO_OFFSET
elif self.sfp_type == 'QSFP_DD':
offset = QSFP_DD_PAGE0
try:
with open(self.eeprom_path, mode="rb", buffering=0) as eeprom:
eeprom.seek(offset)
eeprom.read(1)
except OSError as e:
return "EEPROM read failed ({})".format(e.strerror)
return self.SFP_STATUS_OK

View File

@ -0,0 +1,189 @@
#!/usr/bin/env python
########################################################################
# DellEMC S5296F
#
# 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 = [
['PT Middle Sensor', 0x1],
['ASIC On-board', 0x2],
['PT Left Sensor', 0x3],
['PT Right Sensor', 0x4],
['Inlet Airflow Sensor', 0x5],
['PSU1 Airflow Sensor', 0x7],
['PSU2 Airflow Sensor', 0x8],
['PSU1 Sensor', 0xc],
['PSU2 Sensor', 0xd],
['CPU On-board', 0xe]
]
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("UpperNonCritical")
if not is_valid:
return 0.0
return float(high_threshold)
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
"""
is_valid, high_crit_threshold = self.sensor.get_threshold("UpperCritical")
if not is_valid:
return 0.0
return float(high_crit_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
def get_position_in_parent(self):
"""
Retrieves 1-based relative physical position in parent device.
Returns:
integer: The 1-based relative physical position in parent
device or -1 if cannot determine the position
"""
return self.index
def is_replaceable(self):
"""
Indicate whether this Thermal is replaceable.
Returns:
bool: True if it is replaceable.
"""
return False

View File

@ -0,0 +1,207 @@
#!/usr/bin/env python
########################################################################
#
# DELLEMC S5296F
#
# 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,80,100,120,140,160,180,210,240]
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'.encode())
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 > 0 and seconds <= timer_seconds:
timer_offset = key
seconds = timer_seconds
break
if timer_offset == -1:
return -1
# cpld_version = Component.get_cpld0_version()
# wd_enabled_version = "0.8"
# if cpld_version < wd_enabled_version:
# syslog.syslog(syslog.LOG_ERR,
# 'Older System CPLD ver, Update to 0.8 to support watchdog ')
# return -1
# Extracting 5th to 8th bits for WD timer values
reg_val = self._get_reg_val()
wd_timer_offset = (reg_val >> 4) & 0xf
if wd_timer_offset != timer_offset:
# Setting 5th to 7th bits
# value from timer_offset
self.disarm()
self._set_reg_val((reg_val & 0x07) | (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)
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.
S5296F 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