diff --git a/debian/control b/debian/control index 980855b..b4bd552 100755 --- a/debian/control +++ b/debian/control @@ -54,6 +54,11 @@ Architecture: amd64 Depends: ${misc:Depends} Description: kernel modules for platform devices such as fan, led, sfp +Package: sonic-platform-nokia-ixr7250x4 +Architecture: amd64 +Depends: ${misc:Depends} +Description: kernel modules for platform devices such as fan, led, sfp + Package: sonic-platform-nokia-ixr7220h6-64 Architecture: amd64 Depends: ${misc:Depends} diff --git a/debian/rules b/debian/rules index 37096b7..37d2661 100755 --- a/debian/rules +++ b/debian/rules @@ -20,8 +20,8 @@ KERNEL_SRC := /lib/modules/$(KVERSION) MOD_SRC_DIR := $(shell pwd) MODULE_DIRS := chassis ixr7220h3 ixr7220h4-32d ixr7220h5-32d ixr7220h5-64d ixr7220h4-64d \ - ixr7220h5-64o ixr7250x1b ixr7250x3b ixr7220d4 ixr7220h6-64 -BRIDGE_DRIVER_TARGETS := ixr7250x1b ixr7250x3b + ixr7220h5-64o ixr7250x1b ixr7250x3b ixr7250x4 ixr7220d4 ixr7220h6-64 +BRIDGE_DRIVER_TARGETS := ixr7250x1b ixr7250x3b ixr7250x4 MODULE_DIR := modules UTILS_DIR := utils SERVICE_DIR := service diff --git a/debian/sonic-platform-nokia-ixr7250x4.install b/debian/sonic-platform-nokia-ixr7250x4.install new file mode 100644 index 0000000..412da29 --- /dev/null +++ b/debian/sonic-platform-nokia-ixr7250x4.install @@ -0,0 +1,6 @@ +ixr7250x4/conf/cpuctl.conf etc/modprobe.d +ixr7250x4/scripts/ixr7250x4_platform_init.sh usr/local/bin +ixr7250x4/service/ixr7250x4_platform_init.service etc/systemd/system +ixr7250x4/scripts/nokia-watchdog.sh usr/local/bin +ixr7250x4/service/nokia-watchdog.service etc/systemd/system +ixr7250x4/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/x86_64-nokia_ixr7250_x4-r0 diff --git a/ixr7250x4/conf/cpuctl.conf b/ixr7250x4/conf/cpuctl.conf new file mode 100644 index 0000000..17be3ba --- /dev/null +++ b/ixr7250x4/conf/cpuctl.conf @@ -0,0 +1 @@ +options cpuctl board=2 diff --git a/ixr7250x4/modules/Makefile b/ixr7250x4/modules/Makefile new file mode 100644 index 0000000..8feec61 --- /dev/null +++ b/ixr7250x4/modules/Makefile @@ -0,0 +1 @@ +obj-m := psu_x3b.o psu_eeprom.o fan_eeprom.o fan_led.o max31790_wd.o diff --git a/ixr7250x4/modules/fan_eeprom.c b/ixr7250x4/modules/fan_eeprom.c new file mode 100644 index 0000000..a1d10de --- /dev/null +++ b/ixr7250x4/modules/fan_eeprom.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nokia FAN eeprom decoder + * + * Copyright (C) 2024 Nokia + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EEPROM_NAME "fan_eeprom" +#define EEPROM_LEN 128 +#define FIELD_LEN_MAX 16 + +#define kEeCleiCode 0x1a +#define kMfgAssemblyNum 0x1b +#define kMfgDate 0x17 +#define kMfgSerialNum 0x16 +#define kMfgPartNum 0x15 +#define kHwDirectives 0x05 +#define kPlatforms 0x03 +#define kHwType 0x01 +#define kCSumRec 0x00 + +static const unsigned short normal_i2c[] = { 0x54, I2C_CLIENT_END }; + +static unsigned int debug = 0; +module_param_named(debug, debug, uint, 0); +MODULE_PARM_DESC(debug, "Debug enable(default to 0)"); + +struct menuee_data { + struct mutex lock; + struct i2c_client *client; + char eeprom[EEPROM_LEN]; + char part_number[FIELD_LEN_MAX]; + char mfg_date[FIELD_LEN_MAX]; + char serial_number[FIELD_LEN_MAX]; + char clei[FIELD_LEN_MAX]; + char assembly_num[FIELD_LEN_MAX]; + u32 hw_directives; + u8 platforms; + u8 hw_type; + u8 checksum; +}; + +int cache_eeprom(struct i2c_client *client) +{ + struct menuee_data *ee_data = i2c_get_clientdata(client); + int status; + + status = i2c_smbus_write_byte_data(client, 0, 0); + msleep(1); + for (int i = 0; i < EEPROM_LEN; i++) { + if(i == 0) + ee_data->eeprom[i] = i2c_smbus_read_byte_data(client, 0); + else + ee_data->eeprom[i] = i2c_smbus_read_byte(client); + } + + if (debug) + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, ee_data->eeprom, EEPROM_LEN, true); + + return 0; +} + +int decode_eeprom(struct i2c_client *client) +{ + struct menuee_data *ee_data = i2c_get_clientdata(client); + int i = 0; + int len; + int cpylen; + + while (i < EEPROM_LEN) { + switch (ee_data->eeprom[i]) { + case kEeCleiCode: + i++; + len = ee_data->eeprom[i++]; + if (len <= FIELD_LEN_MAX) + cpylen = len; + else + cpylen = FIELD_LEN_MAX; + memcpy(&ee_data->clei[0], &ee_data->eeprom[i], cpylen); + ee_data->clei[len] = 0; + i += len; + break; + case kMfgDate: + i++; + len = ee_data->eeprom[i++]; + if (len <= FIELD_LEN_MAX) + cpylen = len; + else + cpylen = FIELD_LEN_MAX; + memcpy(&ee_data->mfg_date[0], &ee_data->eeprom[i], cpylen); + ee_data->mfg_date[len] = 0; + i += len; + break; + case kMfgSerialNum: + i++; + len = ee_data->eeprom[i++]; + if (len <= FIELD_LEN_MAX) + cpylen = len; + else + cpylen = FIELD_LEN_MAX; + memcpy(&ee_data->serial_number[0], &ee_data->eeprom[i], cpylen); + ee_data->serial_number[len] = 0; + i += len; + break; + case kMfgPartNum: + i++; + len = ee_data->eeprom[i++]; + if (len <= FIELD_LEN_MAX) + cpylen = len; + else + cpylen = FIELD_LEN_MAX; + memcpy(&ee_data->part_number[0], &ee_data->eeprom[i], cpylen); + ee_data->part_number[len] = 0; + i += len; + break; + case kMfgAssemblyNum: + i++; + len = ee_data->eeprom[i++]; + if (len <= FIELD_LEN_MAX) + cpylen = len; + else + cpylen = FIELD_LEN_MAX; + memcpy(&ee_data->assembly_num[0], &ee_data->eeprom[i], cpylen); + ee_data->assembly_num[len] = 0; + i += len; + break; + case kHwDirectives: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->hw_directives, &ee_data->eeprom[i], 4); + ee_data->hw_directives = swab32(ee_data->hw_directives); + i += len; + break; + case kHwType: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->hw_type, &ee_data->eeprom[i], 1); + i++; + break; + case kPlatforms: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->platforms, &ee_data->eeprom[i], 1); + i += len; + break; + case kCSumRec: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->checksum, &ee_data->eeprom[i], 1); + i += len; + break; + default: + return 0; + } + } + + return 0; +} + +static ssize_t eeprom_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + int i = 0; + int len; + for (i = 0; i < EEPROM_LEN; i++) { + if (data->eeprom[i] == 0) + len += sprintf(buf + len, "."); + else + len += sprintf(buf + len, "%c", data->eeprom[i]); + } + return len; +} + +static ssize_t part_number_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->part_number); +} + +static ssize_t serial_number_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->serial_number); +} + +static ssize_t mfg_date_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->mfg_date); +} + +static ssize_t clei_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->clei); +} + +static ssize_t hw_directives_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "0x%x\n", data->hw_directives); +} + +static ssize_t hw_type_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "0x%x\n", data->hw_type); +} + +static ssize_t platforms_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "0x%x\n", data->platforms); +} + +static ssize_t assembly_num_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->assembly_num); +} + +static DEVICE_ATTR_RO(eeprom); +static DEVICE_ATTR_RO(part_number); +static DEVICE_ATTR_RO(serial_number); +static DEVICE_ATTR_RO(mfg_date); +static DEVICE_ATTR_RO(clei); +static DEVICE_ATTR_RO(hw_directives); +static DEVICE_ATTR_RO(hw_type); +static DEVICE_ATTR_RO(platforms); +static DEVICE_ATTR_RO(assembly_num); + +static struct attribute *eeprom_attributes[] = { + &dev_attr_eeprom.attr, + &dev_attr_part_number.attr, + &dev_attr_serial_number.attr, + &dev_attr_mfg_date.attr, + &dev_attr_clei.attr, + &dev_attr_hw_directives.attr, + &dev_attr_hw_type.attr, + &dev_attr_platforms.attr, + &dev_attr_assembly_num.attr, + NULL +}; + +static const struct attribute_group eeprom_group = { + .attrs = eeprom_attributes, +}; + +static int eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct menuee_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA )) { + dev_info(&client->dev, "i2c_check_functionality failed!\n"); + return -EIO; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + //data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + status = sysfs_create_group(&client->dev.kobj, &eeprom_group); + if (status) { + dev_err(&client->dev, "Cannot create sysfs\n"); + kfree(data); + return status; + } + data->client = client; + + cache_eeprom(client); + decode_eeprom(client); + + return 0; +} + +static void eeprom_remove(struct i2c_client *client) +{ + struct menuee_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &eeprom_group); + kfree(data); + return; +} + +static const struct i2c_device_id eeprom_id[] = { + { EEPROM_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, eeprom_id); + +static struct i2c_driver eeprom_driver = { + .driver = { + .name = EEPROM_NAME, + }, + .probe = eeprom_probe, + .remove = eeprom_remove, + .id_table = eeprom_id, + .address_list = normal_i2c, +}; + +static int __init fan_eeprom_init(void) +{ + return i2c_add_driver(&eeprom_driver); +} + +static void __exit fan_eeprom_exit(void) +{ + i2c_del_driver(&eeprom_driver); +} + +MODULE_DESCRIPTION("FAN eeprom sysfs driver"); +MODULE_AUTHOR("Nokia"); +MODULE_LICENSE("GPL"); + +module_init(fan_eeprom_init); +module_exit(fan_eeprom_exit); \ No newline at end of file diff --git a/ixr7250x4/modules/fan_led.c b/ixr7250x4/modules/fan_led.c new file mode 100644 index 0000000..73305c4 --- /dev/null +++ b/ixr7250x4/modules/fan_led.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nokia 7250-IXR X Fan LED Driver +* +* Copyright (C) 2024 Nokia +* +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "fan_led" + +// REGISTERS ADDRESS MAP +#define REG_MODE1 0x0 +#define REG_MODE2 0x1 +#define REG_PWM0 0x2 +#define REG_PWM1 0x3 +#define REG_PWM2 0x4 +#define REG_PWM3 0x5 +#define REG_GRPPWM 0x6 +#define REG_GRPFREQ 0x7 +#define REG_LEDOUT 0x8 + +#define MODE1_VALUE 0x0 +#define MODE2_VALUE 0x34 +#define LED_OFF 0x40 +#define LED_ON 0x6a +#define LED_BLINK 0x7f + +static const unsigned short led_address_list[] = {0x60, I2C_CLIENT_END}; + +struct fan_led_data { + struct i2c_client *client; + struct mutex update_lock; + int fan_led; +}; + +static void smbus_i2c_write(struct fan_led_data *data, u8 reg, u8 value) +{ + int res = 0; + struct i2c_client *client = data->client; + + mutex_lock(&data->update_lock); + res = i2c_smbus_write_byte_data(client, reg, value); + if (res < 0) { + dev_warn(&client->dev, "I2C WRITE ERROR: reg(0x%02x) err %d\n", reg, res); + } + mutex_unlock(&data->update_lock); +} + +static ssize_t fan_led_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct fan_led_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->fan_led); +} + +static void set_mode(struct fan_led_data *data) +{ + smbus_i2c_write(data, REG_MODE1, MODE1_VALUE); + smbus_i2c_write(data, REG_MODE2, MODE2_VALUE); +} + +static ssize_t fan_led_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct fan_led_data *data = dev_get_drvdata(dev); + u8 usr_val = 0; + + int ret = kstrtou8(buf, 10, &usr_val); + if (ret != 0) { + return ret; + } + if (usr_val > 2) { + return -EINVAL; + } + + set_mode(data); + switch (usr_val) { + case 0: + smbus_i2c_write(data, REG_LEDOUT, LED_OFF); + data->fan_led = 0; + break; + case 1: + smbus_i2c_write(data, REG_PWM0, 0x00); + smbus_i2c_write(data, REG_PWM1, 0xff); + smbus_i2c_write(data, REG_PWM2, 0x00); + smbus_i2c_write(data, REG_LEDOUT, LED_ON); + data->fan_led = 1; + break; + case 2: + smbus_i2c_write(data, REG_PWM0, 0xff); + smbus_i2c_write(data, REG_PWM1, 0x3f); + smbus_i2c_write(data, REG_PWM2, 0x00); + smbus_i2c_write(data, REG_LEDOUT, LED_ON); + data->fan_led = 2; + break; + default: + smbus_i2c_write(data, REG_LEDOUT, LED_OFF); + } + + return count; +} + +// sysfs attributes +static DEVICE_ATTR_RW(fan_led); + +static struct attribute *fan_led_attributes[] = { + &dev_attr_fan_led.attr, + NULL +}; + +static const struct attribute_group fan_led_group = { + .attrs = fan_led_attributes, +}; + +static int fan_led_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int status; + struct fan_led_data *data = NULL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "Fan_LED PROBE ERROR: i2c_check_functionality failed (0x%x)\n", client->addr); + status = -EIO; + goto exit; + } + + dev_info(&client->dev, "Nokia Fan_LED driver found.\n"); + data = kzalloc(sizeof(struct fan_led_data), GFP_KERNEL); + + if (!data) { + dev_err(&client->dev, "Fan_LED PROBE ERROR: Can't allocate memory\n"); + status = -ENOMEM; + goto exit; + } + + data->client = client; + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &fan_led_group); + if (status) { + dev_err(&client->dev, "Fan_LED INIT ERROR: Cannot create sysfs\n"); + goto exit; + } + + set_mode(data); + smbus_i2c_write(data, REG_PWM0, 0x00); + smbus_i2c_write(data, REG_PWM1, 0xff); + smbus_i2c_write(data, REG_PWM2, 0x00); + smbus_i2c_write(data, REG_GRPPWM, 0x80); + smbus_i2c_write(data, REG_GRPFREQ, 0x19); + smbus_i2c_write(data, REG_LEDOUT, LED_BLINK); + data->fan_led = 3; + + return 0; + +exit: + return status; +} + +static void fan_led_remove(struct i2c_client *client) +{ + struct fan_led_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &fan_led_group); + kfree(data); +} + +static const struct i2c_device_id fan_led_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fan_led_id); + +static struct i2c_driver fan_led_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = fan_led_probe, + .remove = fan_led_remove, + .id_table = fan_led_id, + .address_list = led_address_list, +}; + +static int __init fan_led_init(void) +{ + return i2c_add_driver(&fan_led_driver); +} + +static void __exit fan_led_exit(void) +{ + i2c_del_driver(&fan_led_driver); +} + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("NOKIA Fan LED driver"); +MODULE_LICENSE("GPL"); + +module_init(fan_led_init); +module_exit(fan_led_exit); \ No newline at end of file diff --git a/ixr7250x4/modules/max31790_wd.c b/ixr7250x4/modules/max31790_wd.c new file mode 100644 index 0000000..4ffd611 --- /dev/null +++ b/ixr7250x4/modules/max31790_wd.c @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * max31790.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring. + * + * (C) 2015 by Il Han + */ + +#include +#include +#include +#include +#include +#include +#include + +/* MAX31790 registers */ +#define MAX31790_REG_GLOBAL_CONFIG 0x00 +#define MAX31790_REG_FAN_CONFIG(ch) (0x02 + (ch)) +#define MAX31790_REG_FAN_DYNAMICS(ch) (0x08 + (ch)) +#define MAX31790_REG_FAN_FAULT_STATUS2 0x10 +#define MAX31790_REG_FAN_FAULT_STATUS1 0x11 +#define MAX31790_REG_TACH_COUNT(ch) (0x18 + (ch) * 2) +#define MAX31790_REG_PWM_DUTY_CYCLE(ch) (0x30 + (ch) * 2) +#define MAX31790_REG_PWMOUT(ch) (0x40 + (ch) * 2) +#define MAX31790_REG_TARGET_COUNT(ch) (0x50 + (ch) * 2) + +/* Fan Config register bits */ +#define MAX31790_FAN_CFG_RPM_MODE 0x80 +#define MAX31790_FAN_CFG_CTRL_MON 0x10 +#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08 +#define MAX31790_FAN_CFG_TACH_INPUT 0x01 + +/* Fan Dynamics register bits */ +#define MAX31790_FAN_DYN_SR_SHIFT 5 +#define MAX31790_FAN_DYN_SR_MASK 0xE0 +#define SR_FROM_REG(reg) (((reg) & MAX31790_FAN_DYN_SR_MASK) \ + >> MAX31790_FAN_DYN_SR_SHIFT) + +#define FAN_RPM_MIN 120 +#define FAN_RPM_MAX 7864320 + +#define FAN_COUNT_REG_MAX 0xffe0 + +#define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \ + ((60 * (sr) * 8192) / ((reg) >> 4)) : \ + FAN_RPM_MAX) +#define RPM_TO_REG(rpm, sr) ((60 * (sr) * 8192) / ((rpm) * 2)) + +#define NR_CHANNEL 6 + +/* + * Client data (each client gets its own) + */ +struct max31790_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 fan_config[NR_CHANNEL]; + u8 fan_dynamics[NR_CHANNEL]; + u16 fault_status; + u16 tach[NR_CHANNEL * 2]; + u16 pwm[NR_CHANNEL]; + u16 target_count[NR_CHANNEL]; +}; + +static struct max31790_data *max31790_update_device(struct device *dev) +{ + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct max31790_data *ret = data; + int i; + int rv; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_FAULT_STATUS1); + if (rv < 0) + goto abort; + data->fault_status |= rv & 0x3F; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_FAULT_STATUS2); + if (rv < 0) + goto abort; + data->fault_status |= (rv & 0x3F) << 6; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TACH_COUNT(i)); + if (rv < 0) + goto abort; + data->tach[i] = rv; + + if (data->fan_config[i] + & MAX31790_FAN_CFG_TACH_INPUT) { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TACH_COUNT(NR_CHANNEL + + i)); + if (rv < 0) + goto abort; + data->tach[NR_CHANNEL + i] = rv; + } else { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_PWM_DUTY_CYCLE(i)); + if (rv < 0) + goto abort; + data->pwm[i] = rv; + + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TARGET_COUNT(i)); + if (rv < 0) + goto abort; + data->target_count[i] = rv; + } + } + + data->last_updated = jiffies; + data->valid = true; + } + goto done; + +abort: + data->valid = false; + ret = ERR_PTR(rv); + +done: + mutex_unlock(&data->update_lock); + + return ret; +} + +static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 }; + +static u8 get_tach_period(u8 fan_dynamics) +{ + return tach_period[SR_FROM_REG(fan_dynamics)]; +} + +static u8 bits_for_tach_period(int rpm) +{ + u8 bits; + + if (rpm < 500) + bits = 0x0; + else if (rpm < 1000) + bits = 0x1; + else if (rpm < 2000) + bits = 0x2; + else if (rpm < 4000) + bits = 0x3; + else if (rpm < 8000) + bits = 0x4; + else + bits = 0x5; + + return bits; +} + +static int max31790_read_fan(struct device *dev, u32 attr, int channel, + long *val) +{ + struct max31790_data *data = max31790_update_device(dev); + int sr, rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_fan_input: + sr = get_tach_period(data->fan_dynamics[channel % NR_CHANNEL]); + if (data->tach[channel] == FAN_COUNT_REG_MAX) + rpm = 0; + else + rpm = RPM_FROM_REG(data->tach[channel], sr); + *val = rpm; + return 0; + case hwmon_fan_target: + sr = get_tach_period(data->fan_dynamics[channel]); + rpm = RPM_FROM_REG(data->target_count[channel], sr); + *val = rpm; + return 0; + case hwmon_fan_fault: + mutex_lock(&data->update_lock); + *val = !!(data->fault_status & (1 << channel)); + data->fault_status &= ~(1 << channel); + /* + * If a fault bit is set, we need to write into one of the fan + * configuration registers to clear it. Note that this also + * clears the fault for the companion channel if enabled. + */ + if (*val) { + int reg = MAX31790_REG_TARGET_COUNT(channel % NR_CHANNEL); + + i2c_smbus_write_byte_data(data->client, reg, + data->target_count[channel % NR_CHANNEL] >> 8); + } + mutex_unlock(&data->update_lock); + return 0; + case hwmon_fan_enable: + *val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int max31790_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int target_count; + int err = 0; + u8 bits, fan_config; + int sr; + + mutex_lock(&data->update_lock); + + switch (attr) { + case hwmon_fan_target: + val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX); + bits = bits_for_tach_period(val); + data->fan_dynamics[channel] = + ((data->fan_dynamics[channel] & + ~MAX31790_FAN_DYN_SR_MASK) | + (bits << MAX31790_FAN_DYN_SR_SHIFT)); + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(channel), + data->fan_dynamics[channel]); + if (err < 0) + break; + + sr = get_tach_period(data->fan_dynamics[channel]); + target_count = RPM_TO_REG(val, sr); + target_count = clamp_val(target_count, 0x1, 0x7FF); + + data->target_count[channel] = target_count << 5; + + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_TARGET_COUNT(channel), + data->target_count[channel]); + break; + case hwmon_fan_enable: + fan_config = data->fan_config[channel]; + if (val == 0) { + fan_config &= ~MAX31790_FAN_CFG_TACH_INPUT_EN; + } else if (val == 1) { + fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN; + } else { + err = -EINVAL; + break; + } + if (fan_config != data->fan_config[channel]) { + err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel), + fan_config); + if (!err) + data->fan_config[channel] = fan_config; + } + break; + default: + err = -EOPNOTSUPP; + break; + } + + mutex_unlock(&data->update_lock); + + return err; +} + +static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel) +{ + const struct max31790_data *data = _data; + u8 fan_config = data->fan_config[channel % NR_CHANNEL]; + + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + if (channel < NR_CHANNEL || + (fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0444; + return 0; + case hwmon_fan_target: + if (channel < NR_CHANNEL && + !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0644; + return 0; + case hwmon_fan_enable: + if (channel < NR_CHANNEL) + return 0644; + return 0; + default: + return 0; + } +} + +static int max31790_read_pwm(struct device *dev, u32 attr, int channel, + long *val) +{ + struct max31790_data *data = max31790_update_device(dev); + u8 fan_config; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fan_config = data->fan_config[channel]; + + switch (attr) { + case hwmon_pwm_input: + *val = data->pwm[channel] >> 8; + return 0; + case hwmon_pwm_enable: + if (fan_config & MAX31790_FAN_CFG_CTRL_MON) + *val = 0; + else if (fan_config & MAX31790_FAN_CFG_RPM_MODE) + *val = 2; + else + *val = 1; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int max31790_write_pwm(struct device *dev, u32 attr, int channel, + long val) +{ + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u8 fan_config; + int err = 0; + + mutex_lock(&data->update_lock); + + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) { + err = -EINVAL; + break; + } + data->valid = false; + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_PWMOUT(channel), + val << 8); + break; + case hwmon_pwm_enable: + fan_config = data->fan_config[channel]; + if (val == 0) { + fan_config |= MAX31790_FAN_CFG_CTRL_MON; + /* + * Disable RPM mode; otherwise disabling fan speed + * monitoring is not possible. + */ + fan_config &= ~MAX31790_FAN_CFG_RPM_MODE; + } else if (val == 1) { + fan_config &= ~(MAX31790_FAN_CFG_CTRL_MON | MAX31790_FAN_CFG_RPM_MODE); + } else if (val == 2) { + fan_config &= ~MAX31790_FAN_CFG_CTRL_MON; + /* + * The chip sets MAX31790_FAN_CFG_TACH_INPUT_EN on its + * own if MAX31790_FAN_CFG_RPM_MODE is set. + * Do it here as well to reflect the actual register + * value in the cache. + */ + fan_config |= (MAX31790_FAN_CFG_RPM_MODE | MAX31790_FAN_CFG_TACH_INPUT_EN); + } else { + err = -EINVAL; + break; + } + if (fan_config != data->fan_config[channel]) { + err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel), + fan_config); + if (!err) + data->fan_config[channel] = fan_config; + } + break; + default: + err = -EOPNOTSUPP; + break; + } + + mutex_unlock(&data->update_lock); + + return err; +} + +static umode_t max31790_pwm_is_visible(const void *_data, u32 attr, int channel) +{ + const struct max31790_data *data = _data; + u8 fan_config = data->fan_config[channel]; + + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + if (!(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0644; + return 0; + default: + return 0; + } +} + +static int max31790_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return max31790_read_fan(dev, attr, channel, val); + case hwmon_pwm: + return max31790_read_pwm(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int max31790_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_fan: + return max31790_write_fan(dev, attr, channel, val); + case hwmon_pwm: + return max31790_write_pwm(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t max31790_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return max31790_fan_is_visible(data, attr, channel); + case hwmon_pwm: + return max31790_pwm_is_visible(data, attr, channel); + default: + return 0; + } +} + +static ssize_t fan_wd_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int val = 0; + + val = i2c_smbus_read_byte_data(client, MAX31790_REG_GLOBAL_CONFIG); + if (val < 0) { + dev_warn(&client->dev, "Fan Max31790 READ ERROR: reg(0x%02x) err %d\n", MAX31790_REG_GLOBAL_CONFIG, val); + } + + return sprintf(buf, "%d\n", (val & 0x6)>>1); +} + +static ssize_t fan_wd_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) +{ + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + int status = 0; + u8 usr_val = 0; + u8 reg_val; + + status = kstrtou8(buf, 10, &usr_val); + if (status != 0) { + return status; + } + if (usr_val > 3) { + return -EINVAL; + } + + reg_val = i2c_smbus_read_byte_data(client, MAX31790_REG_GLOBAL_CONFIG); + reg_val = reg_val & 0xF9; + usr_val = usr_val << 1; + status = i2c_smbus_write_byte_data(client, MAX31790_REG_GLOBAL_CONFIG, (reg_val | usr_val)); + if (status < 0) { + dev_warn(&client->dev, "Fan Max31790 WRITE ERROR: reg(0x%02x) err %d\n", MAX31790_REG_GLOBAL_CONFIG, status); + } + + return count; +} + +static int fan_init(struct i2c_client *client) +{ + int i, rv; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_GLOBAL_CONFIG); + if (rv < 0) + return rv; + rv = i2c_smbus_write_byte_data(client, + MAX31790_REG_GLOBAL_CONFIG, (rv & 0xf9)); + if (rv < 0) + return rv; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_write_byte_data(client, + MAX31790_REG_PWMOUT(i), 0x80); + if (rv < 0) + return rv; + } + + return 0; +} + +// sysfs attributes +static DEVICE_ATTR_RW(fan_wd); + +static struct attribute *max31790_attributes[] = { + &dev_attr_fan_wd.attr, + NULL +}; + +static const struct attribute_group max31790_attr_group = { + .attrs = max31790_attributes, +}; + +static const struct hwmon_channel_info *max31790_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_ops max31790_hwmon_ops = { + .is_visible = max31790_is_visible, + .read = max31790_read, + .write = max31790_write, +}; + +static const struct hwmon_chip_info max31790_chip_info = { + .ops = &max31790_hwmon_ops, + .info = max31790_info, +}; + +static int max31790_init_client(struct i2c_client *client, + struct max31790_data *data) +{ + int i, rv; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_CONFIG(i)); + if (rv < 0) + return rv; + data->fan_config[i] = rv; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(i)); + if (rv < 0) + return rv; + data->fan_dynamics[i] = rv; + } + + return 0; +} + +static int max31790_probe(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct max31790_data *data; + struct device *hwmon_dev; + int err; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* + * Initialize the max31790 chip + */ + err = max31790_init_client(client, data); + if (err) + return err; + + fan_init(client); + + err = sysfs_create_group(&client->dev.kobj, &max31790_attr_group); + if (err) { + dev_err(&client->dev, "failed to create attribute group\n"); + return err; + } + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &max31790_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max31790_id[] = { + { "max31790_wd", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31790_id); + +static struct i2c_driver max31790_driver = { + .class = I2C_CLASS_HWMON, + .probe_new = max31790_probe, + .driver = { + .name = "max31790_wd", + }, + .id_table = max31790_id, +}; + +module_i2c_driver(max31790_driver); + +MODULE_AUTHOR("Nokia"); +MODULE_DESCRIPTION("MAX31790 driver with WD"); +MODULE_LICENSE("GPL"); diff --git a/ixr7250x4/modules/psu_eeprom.c b/ixr7250x4/modules/psu_eeprom.c new file mode 100644 index 0000000..395fe40 --- /dev/null +++ b/ixr7250x4/modules/psu_eeprom.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nokia PSU eeprom decoder + * + * Copyright (C) 2024 Nokia + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define EEPROM_NAME "psu_eeprom" +#define EEPROM_LEN 128 +#define FIELD_LEN_MAX 16 + +#define kEeCleiCode 0x1a +#define kMfgDate 0x17 +#define kMfgSerialNum 0x16 +#define kMfgPartNum 0x15 +#define kHwDirectives 0x05 +#define kHwType 0x01 +#define kCSumRec 0x00 + +static const unsigned short normal_i2c[] = { 0x53, I2C_CLIENT_END }; + +static unsigned int debug = 0; +module_param_named(debug, debug, uint, 0); +MODULE_PARM_DESC(debug, "Debug enable(default to 0)"); + +struct menuee_data { + struct mutex lock; + struct i2c_client *client; + char eeprom[EEPROM_LEN]; + char part_number[FIELD_LEN_MAX]; + char mfg_date[FIELD_LEN_MAX]; + char serial_number[FIELD_LEN_MAX]; + char clei[FIELD_LEN_MAX]; + u32 hw_directives; + u8 hw_type; + u8 checksum; +}; + +int cache_eeprom(struct i2c_client *client) +{ + struct menuee_data *ee_data = i2c_get_clientdata(client); + + for (int i = 0; i < EEPROM_LEN; i++) { + if(i == 0) + ee_data->eeprom[i] = i2c_smbus_read_byte_data(client,0); + else + ee_data->eeprom[i] = i2c_smbus_read_byte(client); + } + + if (debug) + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, ee_data->eeprom, EEPROM_LEN, true); + + return 0; +} + +int decode_eeprom(struct i2c_client *client) +{ + struct menuee_data *ee_data = i2c_get_clientdata(client); + int i = 0; + u8 len; + + while (i < EEPROM_LEN) { + switch (ee_data->eeprom[i]) { + case kEeCleiCode: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->clei[0], &ee_data->eeprom[i], len); + ee_data->clei[len] = 0; + i += len; + break; + case kMfgDate: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->mfg_date[0], &ee_data->eeprom[i], len); + ee_data->mfg_date[len] = 0; + i += len; + break; + case kMfgSerialNum: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->serial_number[0], &ee_data->eeprom[i], len); + ee_data->serial_number[len] = 0; + i += len; + break; + case kMfgPartNum: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->part_number[0], &ee_data->eeprom[i], len); + ee_data->part_number[len] = 0; + i += len; + break; + case kHwDirectives: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->hw_directives, &ee_data->eeprom[i], len); + ee_data->hw_directives = swab32(ee_data->hw_directives); + i += len; + break; + case kHwType: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->hw_type, &ee_data->eeprom[i], len); + i += len; + break; + case kCSumRec: + i++; + len = ee_data->eeprom[i++]; + memcpy(&ee_data->checksum, &ee_data->eeprom[i], len); + i += len; + break; + default: + return 0; + } + } + + return 0; +} + +static ssize_t eeprom_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->eeprom); +} + +static ssize_t part_number_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->part_number); +} + +static ssize_t serial_number_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->serial_number); +} + +static ssize_t mfg_date_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->mfg_date); +} + +static ssize_t clei_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->clei); +} + +static ssize_t hw_directives_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "0x%x\n", data->hw_directives); +} + +static ssize_t hw_type_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct menuee_data *data = dev_get_drvdata(dev); + return sprintf(buf, "0x%x\n", data->hw_type); +} + +static DEVICE_ATTR_RO(eeprom); +static DEVICE_ATTR_RO(part_number); +static DEVICE_ATTR_RO(serial_number); +static DEVICE_ATTR_RO(mfg_date); +static DEVICE_ATTR_RO(clei); +static DEVICE_ATTR_RO(hw_directives); +static DEVICE_ATTR_RO(hw_type); + +static struct attribute *eeprom_attributes[] = { + &dev_attr_eeprom.attr, + &dev_attr_part_number.attr, + &dev_attr_serial_number.attr, + &dev_attr_mfg_date.attr, + &dev_attr_clei.attr, + &dev_attr_hw_directives.attr, + &dev_attr_hw_type.attr, + NULL +}; + +static const struct attribute_group eeprom_group = { + .attrs = eeprom_attributes, +}; + +static int eeprom_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct menuee_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_info(&client->dev, "i2c_check_functionality failed!\n"); + return -EIO; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + return -ENOMEM; + } + + //data->client = client; + mutex_init(&data->lock); + i2c_set_clientdata(client, data); + + status = sysfs_create_group(&client->dev.kobj, &eeprom_group); + if (status) { + dev_err(&client->dev, "Cannot create sysfs\n"); + kfree(data); + return status; + } + data->client = client; + + cache_eeprom(client); + decode_eeprom(client); + + return 0; +} + +static void eeprom_remove(struct i2c_client *client) +{ + struct menuee_data *data = i2c_get_clientdata(client); + sysfs_remove_group(&client->dev.kobj, &eeprom_group); + kfree(data); + return; +} + +static const struct i2c_device_id eeprom_id[] = { + { EEPROM_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, eeprom_id); + +static struct i2c_driver eeprom_driver = { + .driver = { + .name = EEPROM_NAME, + }, + .probe = eeprom_probe, + .remove = eeprom_remove, + .id_table = eeprom_id, + .address_list = normal_i2c, +}; + +static int __init psu_eeprom_init(void) +{ + return i2c_add_driver(&eeprom_driver); +} + +static void __exit psu_eeprom_exit(void) +{ + i2c_del_driver(&eeprom_driver); +} + +MODULE_DESCRIPTION("PSU eeprom sysfs driver"); +MODULE_AUTHOR("Nokia"); +MODULE_LICENSE("GPL"); + +module_init(psu_eeprom_init); +module_exit(psu_eeprom_exit); \ No newline at end of file diff --git a/ixr7250x4/modules/psu_x3b.c b/ixr7250x4/modules/psu_x3b.c new file mode 100644 index 0000000..2c56449 --- /dev/null +++ b/ixr7250x4/modules/psu_x3b.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Nokia PSU Driver + * + * Copyright (C) 2024 Nokia + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_FAN_DUTY_CYCLE 100 +#define PMBUS_CODE_STATUS_WORD 0x79 +#define PSU_DRIVER_NAME "psu_x3b" + +static const unsigned short normal_i2c[] = { 0x5b, I2C_CLIENT_END }; + +struct x3b_psu_data { + struct device *hwmon_dev; + struct mutex update_lock; + char valid; + unsigned long last_updated; /* In jiffies */ + + /* Registers value */ + u8 vout_mode; + u16 in1_input; + u16 in2_input; + u16 curr1_input; + u16 curr2_input; + u16 power1_input; + u16 power2_input; + u16 temp_input[2]; + u8 fan_target; + u16 fan_duty_cycle_input[2]; + u16 fan_speed_input[2]; +}; + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask); +static ssize_t set_fan_duty_cycle_input(struct device *dev, struct device_attribute \ + *dev_attr, const char *buf, size_t count); +static ssize_t for_linear_data(struct device *dev, struct device_attribute \ + *dev_attr, char *buf); +static ssize_t for_fan_target(struct device *dev, struct device_attribute \ + *dev_attr, char *buf); +static ssize_t for_vout_data(struct device *dev, struct device_attribute \ + *dev_attr, char *buf); +static int x3b_psu_read_byte(struct i2c_client *client, u8 reg); +static int x3b_psu_read_word(struct i2c_client *client, u8 reg); +static int x3b_psu_write_word(struct i2c_client *client, u8 reg, \ + u16 value); +static struct x3b_psu_data *x3b_psu_update_device(struct device *dev); + +enum x3b_psu_sysfs_attributes { + PSU_V_IN, + PSU_V_OUT, + PSU_I_IN, + PSU_I_OUT, + PSU_P_IN, + PSU_P_OUT, + PSU_TEMP1_INPUT, + PSU_FAN1_FAULT, + PSU_FAN1_DUTY_CYCLE, + PSU_FAN1_SPEED, +}; + +static int two_complement_to_int(u16 data, u8 valid_bit, int mask) +{ + u16 valid_data = data & mask; + bool is_negative = valid_data >> (valid_bit - 1); + + return is_negative ? (-(((~valid_data) & mask) + 1)) : valid_data; +} + +static ssize_t set_fan_duty_cycle_input(struct device *dev, struct device_attribute \ + *dev_attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct i2c_client *client = to_i2c_client(dev); + struct x3b_psu_data *data = i2c_get_clientdata(client); + int nr = (attr->index == PSU_FAN1_DUTY_CYCLE) ? 0 : 1; + long speed; + int error; + + error = kstrtol(buf, 10, &speed); + if (error) + return error; + + if (speed < 0 || speed > MAX_FAN_DUTY_CYCLE) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->fan_duty_cycle_input[nr] = speed; + x3b_psu_write_word(client, 0x3B + nr, data->fan_duty_cycle_input[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t for_linear_data(struct device *dev, struct device_attribute \ + *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct x3b_psu_data *data = x3b_psu_update_device(dev); + + u16 value = 0; + int exponent, mantissa; + int multiplier = 1000; + + switch (attr->index) { + case PSU_V_IN: + value = data->in1_input; + break; + case PSU_V_OUT: + value = data->in2_input; + break; + case PSU_I_IN: + value = data->curr1_input; + break; + case PSU_I_OUT: + value = data->curr2_input; + break; + case PSU_P_IN: + value = data->power1_input; + multiplier = 1000*1000; + break; + case PSU_P_OUT: + value = data->power2_input; + multiplier = 1000*1000; + break; + case PSU_TEMP1_INPUT: + value = data->temp_input[0]; + break; + case PSU_FAN1_DUTY_CYCLE: + multiplier = 1; + value = data->fan_duty_cycle_input[0]; + break; + case PSU_FAN1_SPEED: + multiplier = 1; + value = data->fan_speed_input[0]; + break; + default: + break; + } + + exponent = two_complement_to_int(value >> 11, 5, 0x1f); + mantissa = two_complement_to_int(value & 0x7ff, 11, 0x7ff); + + return (exponent >= 0) ? sprintf(buf, "%d\n", \ + (mantissa << exponent) * multiplier) : \ + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static ssize_t for_fan_target(struct device *dev, struct device_attribute \ + *dev_attr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); + struct x3b_psu_data *data = x3b_psu_update_device(dev); + + u8 shift = (attr->index == PSU_FAN1_FAULT) ? 7 : 6; + + return sprintf(buf, "%d\n", data->fan_target >> shift); +} + +static ssize_t for_vout_data(struct device *dev, struct device_attribute \ + *dev_attr, char *buf) +{ + struct x3b_psu_data *data = x3b_psu_update_device(dev); + int exponent = 0; + int mantissa = 0; + int multiplier = 1000; + + exponent = two_complement_to_int(data->vout_mode, 5, 0x1f); + mantissa = data->in2_input; + + return (exponent > 0) ? sprintf(buf, "%d\n", \ + mantissa * (1 << exponent)) : \ + sprintf(buf, "%d\n", (mantissa * multiplier) / (1 << -exponent)); +} + +static int x3b_psu_read_byte(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int x3b_psu_read_word(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_word_data(client, reg); +} + +static int x3b_psu_write_word(struct i2c_client *client, u8 reg, \ + u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags |= I2C_CLIENT_PEC, + I2C_SMBUS_WRITE, reg, + I2C_SMBUS_WORD_DATA, &data); +} + +struct reg_data_byte { + u8 reg; + u8 *value; +}; + +struct reg_data_word { + u8 reg; + u16 *value; +}; + +static struct x3b_psu_data *x3b_psu_update_device( \ + struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct x3b_psu_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated)) { + int i, status; + struct reg_data_byte regs_byte[] = { + {0x20, &data->vout_mode}, + {0x81, &data->fan_target} + }; + struct reg_data_word regs_word[] = { + {0x88, &data->in1_input}, + {0x8b, &data->in2_input}, + {0x89, &data->curr1_input}, + {0x8c, &data->curr2_input}, + {0x96, &data->power2_input}, + {0x97, &data->power1_input}, + {0x8d, &(data->temp_input[0])}, + {0x8e, &(data->temp_input[1])}, + {0x3b, &(data->fan_duty_cycle_input[0])}, + {0x90, &(data->fan_speed_input[0])}, + }; + + dev_dbg(&client->dev, "start data update\n"); + + /* one milliseconds from now */ + data->last_updated = jiffies + HZ / 1000; + + for (i = 0; i < ARRAY_SIZE(regs_byte); i++) { + status = x3b_psu_read_byte(client, + regs_byte[i].reg); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_byte[i].reg, status); + *(regs_byte[i].value) = 0; + } else { + *(regs_byte[i].value) = status; + } + } + + for (i = 0; i < ARRAY_SIZE(regs_word); i++) { + status = x3b_psu_read_word(client, + regs_word[i].reg); + if (status < 0) { + dev_dbg(&client->dev, "reg %d, err %d\n", + regs_word[i].reg, status); + *(regs_word[i].value) = 0; + } else { + *(regs_word[i].value) = status; + } + } + + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static ssize_t psu_status_show(struct device *dev, struct device_attribute \ + *dev_attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int retval; + + retval = x3b_psu_read_word(client, PMBUS_CODE_STATUS_WORD); + + return sprintf(buf, "%d\n", retval); +} + +/* sysfs attributes for hwmon */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, for_linear_data, NULL, PSU_V_IN); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, for_vout_data, NULL, PSU_V_OUT); +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, for_linear_data, NULL, PSU_I_IN); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, for_linear_data, NULL, PSU_I_OUT); +static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, for_linear_data, NULL, PSU_P_IN); +static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, for_linear_data, NULL, PSU_P_OUT); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, for_linear_data, NULL, PSU_TEMP1_INPUT); +static SENSOR_DEVICE_ATTR(fan1_target, S_IRUGO, for_fan_target, NULL, PSU_FAN1_FAULT); +static SENSOR_DEVICE_ATTR(fan1_set_percentage, S_IWUSR | S_IRUGO, \ + for_linear_data, set_fan_duty_cycle_input, PSU_FAN1_DUTY_CYCLE); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, for_linear_data, NULL, PSU_FAN1_SPEED); +static SENSOR_DEVICE_ATTR(psu_status, S_IRUGO, psu_status_show, NULL, 0); + +static struct attribute *x3b_psu_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power2_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_fan1_target.dev_attr.attr, + &sensor_dev_attr_fan1_set_percentage.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_psu_status.dev_attr.attr, + NULL +}; + +static const struct attribute_group x3b_psu_group = { + .attrs = x3b_psu_attributes, +}; + +static int x3b_psu_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct x3b_psu_data *data; + int status; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) { + status = -EIO; + dev_info(&client->dev, "i2c_check_functionality failed\n"); + goto exit; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + status = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + client->flags |= I2C_CLIENT_SCCB; + data->valid = 0; + mutex_init(&data->update_lock); + + status = sysfs_create_group(&client->dev.kobj, &x3b_psu_group); + if (status) + goto exit_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register_with_groups(&client->dev, PSU_DRIVER_NAME, NULL, NULL); + if (IS_ERR(data->hwmon_dev)) { + status = PTR_ERR(data->hwmon_dev); + goto exit_hwmon_device_register; + } + + return 0; + +exit_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, &x3b_psu_group); +exit_sysfs_create_group: + kfree(data); +exit: + return status; +} + +static void x3b_psu_remove(struct i2c_client *client) +{ + struct x3b_psu_data *data = i2c_get_clientdata(client); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &x3b_psu_group); + kfree(data); +} + +static const struct i2c_device_id x3b_psu_id[] = { + { PSU_DRIVER_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, x3b_psu_id); + +static struct i2c_driver x3b_psu_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = PSU_DRIVER_NAME, + }, + .probe = x3b_psu_probe, + .remove = x3b_psu_remove, + .id_table = x3b_psu_id, + .address_list = normal_i2c, +}; + +static int __init x3b_psu_init(void) +{ + return i2c_add_driver(&x3b_psu_driver); +} + +static void __exit x3b_psu_exit(void) +{ + i2c_del_driver(&x3b_psu_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PSU Driver"); +MODULE_AUTHOR("Nokia"); + +module_init(x3b_psu_init); +module_exit(x3b_psu_exit); diff --git a/ixr7250x4/scripts/ixr7250x4_platform_init.sh b/ixr7250x4/scripts/ixr7250x4_platform_init.sh new file mode 100755 index 0000000..b9e4cc0 --- /dev/null +++ b/ixr7250x4/scripts/ixr7250x4_platform_init.sh @@ -0,0 +1,158 @@ +#!/bin/bash + +# platform init script for Nokia IXR7250 X4 + +# Load required kernel-mode drivers +load_kernel_drivers() { + echo "Loading Kernel Drivers" + depmod -a + rmmod amd-xgbe + rmmod igb + rmmod i2c-piix4 + rmmod i2c_designware_platform + modprobe igb + modprobe amd-xgbe + modprobe i2c_designware_platform + modprobe i2c-piix4 + modprobe i2c-smbus + modprobe i2c-dev + modprobe i2c-mux + modprobe cpuctl + modprobe rtc_ds1307 + modprobe optoe + modprobe at24 + modprobe max31790_wd + modprobe jc42 + modprobe psu_x3b + modprobe fan_eeprom + modprobe psu_eeprom + modprobe fan_led + modprobe pcon +} + +x4_profile() +{ + MAC_ADDR=$(sudo decode-syseeprom -m) + if [ $? -eq 0 ]; then + sed -i "s/switchMacAddress=.*/switchMacAddress=$MAC_ADDR/g" /usr/share/sonic/device/x86_64-nokia_ixr7250_x4-r0/Nokia-IXR7250-X4/profile.ini + echo "Nokia-IXR7250-X4: Updated switch mac address ${MAC_ADDR}" + fi +} + +file_exists() { + # Wait 10 seconds max till file exists + for((i=0; i<10; i++)); + do + if [ -f $1 ]; then + return 1 + fi + sleep 1 + done + return 0 + } + +dev_conf_init() { + CONF_FILE=/var/run/sonic-platform-nokia/devices.conf + mkdir -p /var/run/sonic-platform-nokia/ + cat /dev/null > CONF_FILE + + echo board=x4 >> $CONF_FILE + + echo "cpctl=/sys/bus/pci/drivers/cpuctl/0000:01:00.0/" >> $CONF_FILE + echo "ioctl=/sys/bus/pci/drivers/cpuctl/0000:05:00.0/" >> $CONF_FILE + + # pcons on x4 + echo pcon 0x74 > /sys/bus/i2c/devices/i2c-23/new_device + echo pcon 0x74 > /sys/bus/i2c/devices/i2c-24/new_device + echo pconm 0x74 > /sys/bus/i2c/devices/i2c-17/new_device + + file_exists /sys/bus/i2c/drivers/pcon/23-0074/name + file_exists /sys/bus/i2c/drivers/pcon/24-0074/name + file_exists /sys/bus/i2c/drivers/pcon/17-0074/name + + PCON0_HWMON=$(ls /sys/bus/i2c/drivers/pcon/23-0074/hwmon/) + echo "pcon0=/sys/class/hwmon/${PCON0_HWMON}" >> $CONF_FILE + PCON1_HWMON=$(ls /sys/bus/i2c/drivers/pcon/24-0074/hwmon/) + echo "pcon1=/sys/class/hwmon/${PCON1_HWMON}" >> $CONF_FILE + PCON2_HWMON=$(ls /sys/bus/i2c/drivers/pcon/17-0074/hwmon/) + echo "pcon2=/sys/class/hwmon/${PCON2_HWMON}" >> $CONF_FILE +} + +# Install kernel drivers required for i2c bus access +load_kernel_drivers + +echo m41t11 0x68 > /sys/bus/i2c/devices/i2c-7/new_device + +# Enumerate system eeprom +file_exists /sys/bus/i2c/devices/i2c-1/new_device +echo 24c64 0x54 > /sys/bus/i2c/devices/i2c-1/new_device + +hwclock -s -f /dev/rtc1 + +dev_conf_init + +if type sets_setup &> /dev/null ; then + sets_setup -d +fi + +if type asic_rov_config &> /dev/null ; then + asic_rov_config -v +fi + +# take asics out of reset +/etc/init.d/opennsl-modules stop +echo 1 > /sys/bus/pci/drivers/cpuctl/0000:05:00.0/jer_reset_seq +sleep 1 +/etc/init.d/opennsl-modules start + +echo jc42 0x18 > /sys/bus/i2c/devices/i2c-0/new_device +echo jc42 0x19 > /sys/bus/i2c/devices/i2c-0/new_device +echo tmp75 0x49 > /sys/bus/i2c/devices/i2c-1/new_device +echo tmp421 0x1e > /sys/bus/i2c/devices/i2c-7/new_device +echo tmp75 0x49 > /sys/bus/i2c/devices/i2c-19/new_device +echo tmp75 0x4a > /sys/bus/i2c/devices/i2c-19/new_device +echo tmp75 0x4b > /sys/bus/i2c/devices/i2c-19/new_device + +#fan +for idx in {1..3} +do + prs=$(cat /sys/bus/pci/devices/0000:01:00.0/fandraw_${idx}_prs) + if [ "$prs" == "0" ]; then + echo max31790_wd 0x20 > /sys/bus/i2c/devices/i2c-$((${idx}+10))/new_device + echo fan_led 0x60 > /sys/bus/i2c/devices/i2c-$((${idx}+10))/new_device + echo fan_eeprom 0x54 > /sys/bus/i2c/devices/i2c-$((${idx}+10))/new_device + fi +done + +# PSU +echo psu_x3b 0x5b > /sys/bus/i2c/devices/i2c-14/new_device +echo psu_x3b 0x5b > /sys/bus/i2c/devices/i2c-15/new_device +echo psu_eeprom 0x53 > /sys/bus/i2c/devices/i2c-14/new_device +echo psu_eeprom 0x53 > /sys/bus/i2c/devices/i2c-15/new_device + +for num in {27..62}; do + echo optoe1 0x50 > /sys/bus/i2c/devices/i2c-${num}/new_device +done + +for num in {27..58}; do + echo 300 > /sys/bus/i2c/devices/${num}-0050/write_timeout +done + +file_exists /sys/bus/i2c/devices/1-0054/eeprom +status=$? +if [ "$status" == "1" ]; then + chmod 644 /sys/bus/i2c/devices/1-0054/eeprom + x4_profile +else + echo "SYSEEPROM file not foud" +fi + +if type pcon_cmds &> /dev/null ; then + pcon_cmds -v -r /var/run/sonic-platform-nokia/pcon_reboot_reason +fi + +if type sets_setup &> /dev/null ; then + sets_setup --wait-lock +fi + +exit 0 diff --git a/ixr7250x4/scripts/nokia-watchdog.sh b/ixr7250x4/scripts/nokia-watchdog.sh new file mode 100755 index 0000000..10eaa29 --- /dev/null +++ b/ixr7250x4/scripts/nokia-watchdog.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# err_func() { +# echo "Error raised! " `date -u` >> /var/log/nokia-watchdog-$suf.log +# exec 5>&- +#} + +# trap 'err_func' ERR + +app_count=0 +min_app_count_failures=3 +function platform_health_monitor() +{ + app_count=0 +} + +watchdog_fail=1 +watchdog_sleep_time=20 + +start_date=`date -u` +MAX_RETAINED_LOGS=10 + +suf=`date -u "+%Y%m%d%H%M%S"` + +ls -t /var/log/nokia-watchdog-init*.log| cat -n | while read n f; do if [ $n -gt $MAX_RETAINED_LOGS ]; then echo "pruning $n $f"; rm $f; else cp -p $f `printf "/tmp/nokia-watchdog-init%d.log" $n`; rm $f; fi; done +ls -t /var/log/nokia-watchdog-last*.log| cat -n | while read n f; do if [ $n -gt $MAX_RETAINED_LOGS ]; then echo "pruning $n $f"; rm $f; else cp -p $f `printf "/tmp/nokia-watchdog-last%d.log" $n`; rm $f; fi; done +ls -t /var/log/nokia-watchdog*.log| cat -n | while read n f; do if [ $n -gt $MAX_RETAINED_LOGS ]; then echo "pruning $n $f"; rm $f; else cp -p $f `printf "/tmp/nokia-watchdog%d.log" $n`; rm $f; fi; done + +cp -p /tmp/nokia-watchdog* /var/log/ +rm -f /tmp/nokia-watchdog* + +wd_init_log_file=/var/log/nokia-watchdog-init.log +echo "Started at $start_date" > $wd_init_log_file +ls -las /dev/watch* >> $wd_init_log_file +stat /dev/watch* >> $wd_init_log_file + +# check for and remove sp5100 driver just in case blacklist somehow not present +lsmod | grep 5100 >> $wd_init_log_file +modprobe -v -r sp5100_tco >> $wd_init_log_file +sleep 1 +if [ ! -e /dev/watchdog ]; then + echo "no /dev/watchdog present" >> $wd_init_log_file +else + echo "/dev/watchdog present" >> $wd_init_log_file +fi + +echo "done check for /dev/watchdog at " `date -u` >> $wd_init_log_file + +ls -las /dev/watch* >> $wd_init_log_file +stat /dev/watch* >> $wd_init_log_file + +depmod -A + +echo "before modprobe nokia_gpio_wdt at " `date -u` >> $wd_init_log_file +modprobe -v nokia_gpio_wdt >> $wd_init_log_file +while [ "$?" != "0" ] +do + sleep 1 + modprobe -v nokia_gpio_wdt >> $wd_init_log_file +done +echo "after modprobe nokia_gpio_wdt at " `date -u` >> $wd_init_log_file + +sleep 2 + +ls -las /dev/watch* >> $wd_init_log_file +stat /dev/watch* >> $wd_init_log_file + +exec {desc}>/dev/watchdog +echo "w" >&${desc} + +echo "first kick done using desc $desc at " `date -u` >> $wd_init_log_file + +# find /host -name nokia_gpio_wdt.ko >> $wd_init_log_file +# find /host -name blacklist.conf >> $wd_init_log_file + +lsmod | grep nokia >> $wd_init_log_file +lsmod | grep 5100 >> $wd_init_log_file +dmesg | grep nokia >> $wd_init_log_file +sync +echo "sync done " `date -u` >> $wd_init_log_file + +wd_log_file=/var/log/nokia-watchdog.log +wd_temp_file=/tmp/nokia-watchdog.tmp + +echo "start watchdog monitoring" > $wd_log_file + +#Option to disable watchdog +source /host/machine.conf +disable_watchdog_hm=1 +if [ "$disable_watchdog_hm" != "1" ]; then + echo "Watchdog platform health-monitoring is enabled" >> $wd_init_log_file +else + echo "Watchdog platform health-monitoring is disabled" >> $wd_init_log_file +fi +n_kicks=2 +while [ 1 ] ; do + #Process health monitor + if [[ $n_kicks -le 0 ]]; then + if [ "$disable_watchdog_hm" != "1" ]; then + platform_health_monitor + if [[ $app_count -gt 0 ]]; then + echo "platform process health monitor failed with app_count `expr $app_count` at " `date -u` >> $wd_init_log_file + fi + if [[ $app_count -ge $min_app_count_failures ]]; then + echo "missed too many health monitor iters. System will reboot soon." `date -u` >> $wd_init_log_file + watchdog_fail=1 + continue + fi + fi + else + echo "Skipping platform health monitor check for $n_kicks at " `date -u` >> $wd_init_log_file + n_kicks=$(( n_kicks - 1 )) + fi + + if [[ $watchdog_fail -eq 1 ]]; then + echo "enable watchdog kick" `date -u` >> $wd_init_log_file + watchdog_fail=0 + fi + + #Kick watchdog + echo "w" >&${desc} + echo $start_date > $wd_temp_file + echo `date -u` >> $wd_temp_file + cp $wd_temp_file $wd_log_file + + sleep $watchdog_sleep_time +done diff --git a/ixr7250x4/service/ixr7250x4_platform_init.service b/ixr7250x4/service/ixr7250x4_platform_init.service new file mode 100644 index 0000000..e0c6580 --- /dev/null +++ b/ixr7250x4/service/ixr7250x4_platform_init.service @@ -0,0 +1,14 @@ +[Unit] +Description=Nokia-7250-IXR-X4 Platform Service +After=sysinit.target +Before=syncd.service determine-reboot-cause.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/ixr7250x4_platform_init.sh +StandardOutput=journal +StandardError=journal +SyslogIdentifier=ixr7250x4_platform_init + +[Install] +WantedBy=multi-user.target diff --git a/ixr7250x4/service/nokia-watchdog.service b/ixr7250x4/service/nokia-watchdog.service new file mode 100644 index 0000000..4505d6d --- /dev/null +++ b/ixr7250x4/service/nokia-watchdog.service @@ -0,0 +1,10 @@ +[Unit] +Description=Nokia IXR-7250 watchdog service +After=sysinit.target + +[Service] +Type=simple +ExecStart=nice -n -10 /bin/bash /usr/local/bin/nokia-watchdog.sh start + +[Install] +WantedBy=multi-user.target diff --git a/ixr7250x4/setup.py b/ixr7250x4/setup.py new file mode 100755 index 0000000..b8fe5a9 --- /dev/null +++ b/ixr7250x4/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import os +import sys +from setuptools import setup +os.listdir + +setup( + name='sonic_platform', + version='1.0', + description='Module to initialize Nokia 7250 IXR X4 platforms', + + packages=[ + 'sonic_platform', + 'sonic_platform.test' + ], + + package_dir={ + 'sonic_platform': 'sonic_platform' + } +) diff --git a/ixr7250x4/sonic_platform/__init__.py b/ixr7250x4/sonic_platform/__init__.py new file mode 100644 index 0000000..290d8cd --- /dev/null +++ b/ixr7250x4/sonic_platform/__init__.py @@ -0,0 +1,2 @@ +__all__ = ["platform"] +from sonic_platform import * diff --git a/ixr7250x4/sonic_platform/chassis.py b/ixr7250x4/sonic_platform/chassis.py new file mode 100644 index 0000000..a6f0855 --- /dev/null +++ b/ixr7250x4/sonic_platform/chassis.py @@ -0,0 +1,423 @@ +""" + NOKIA 7250 IXR-X4 + + Module contains an implementation of SONiC Platform Base API and + provides the platform information +""" + +try: + import os + import sys + from datetime import datetime, timezone + from sonic_platform_base.chassis_base import ChassisBase + from sonic_platform.sfp import Sfp + from sonic_platform.eeprom import Eeprom + from sonic_platform.fan import Fan + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from .fan_drawer import RealDrawer + from sonic_platform.psu import Psu + from sonic_platform.thermal import Thermal + from sonic_platform.component import Component + from sonic_platform.sfp_event import SfpEvent + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +PORT_START = 1 +PORT_NUM = 32 +PORT_END = 32 +PORT_I2C_START = 27 +REG_DIR = "/sys/bus/pci/devices/0000:01:00.0/" + +# Device counts +FAN_DRAWERS_NUM = 3 +FANS_PER_DRAWER = 4 +PSU_NUM = 2 +THERMAL_NUM = 12 +COMPONENT_NUM = 3 +MAX_SELECT_DELAY = 10 +SYSLOG_IDENTIFIER = "chassis" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) + +class Chassis(ChassisBase): + """ + Nokia platform-specific Chassis class + customized for the 7250 X4 platform. + """ + + def __init__(self): + ChassisBase.__init__(self) + self.system_led_supported_color = ['off', 'green', 'amber', 'blue', + 'green_blink', 'amber_blink'] + + # Port numbers for SFP List Initialization + self.PORT_START = PORT_START + self.PORT_END = PORT_END + self._watchdog = None + self.sfp_event = None + self.max_select_event_returned = None + self._sys_led = 'blue' + + # Verify optoe driver PORT eeprom devices were enumerated and exist + # then create the sfp nodes + eeprom_path = "/sys/bus/i2c/devices/{}-0050/eeprom" + for index in range(PORT_START, PORT_START + PORT_NUM): + port_i2c_map = PORT_I2C_START + index - 1 + port_eeprom_path = eeprom_path.format(port_i2c_map) + if not os.path.exists(port_eeprom_path): + sonic_logger.log_info(f"path {port_eeprom_path} didnt exist") + sfp_node = Sfp(index, 'QSFPDD', port_eeprom_path, port_i2c_map) + self._sfp_list.append(sfp_node) + + self.sfp_event_initialized = False + + # Instantiate system eeprom object + self._eeprom = Eeprom(False, 0, False, 0) + + # Construct lists fans, power supplies, thermals & components + for i in range(THERMAL_NUM): + thermal = Thermal(i, self._sfp_list) + self._thermal_list.append(thermal) + + drawer_num = FAN_DRAWERS_NUM + fan_num_per_drawer = FANS_PER_DRAWER + drawer_ctor = RealDrawer + for drawer_index in range(drawer_num): + drawer = drawer_ctor(drawer_index) + self._fan_drawer_list.append(drawer) + for fan_index in range(fan_num_per_drawer): + fan = Fan(fan_index, drawer_index) + drawer._fan_list.append(fan) + self._fan_list.append(fan) + + for i in range(PSU_NUM): + psu = Psu(i) + self._psu_list.append(psu) + + for i in range(COMPONENT_NUM): + component = Component(i) + self._component_list.append(component) + + def get_sfp(self, index): + """ + Retrieves sfp represented by (1-based) index + Args: + index: An integer, the index (1-based) of the sfp to retrieve. + The index should be the sequence of physical SFP ports in a + chassis starting from 1. + + Returns: + An object dervied from SfpBase representing the specified sfp + """ + sfp = None + + try: + # The index will start from 1 + sfp = self._sfp_list[index-1] + except IndexError: + sys.stderr.write("SFP index {} out of range (1-{})\n".format( + index, len(self._sfp_list))) + return sfp + + def get_change_event(self, timeout=0): + """ + Returns a nested dictionary containing all devices which have + experienced a change at chassis level + + Args: + timeout: Timeout in milliseconds (optional). If timeout == 0, + this method will block until a change is detected. + + Returns: + (bool, dict): + - True if call successful, False if not; + - A nested dictionary where key is a device type, + value is a dictionary with key:value pairs in the format of + {'device_id':'device_event'}, + where device_id is the device ID for this device and + device_event, + status='1' represents device inserted, + status='0' represents device removed. + Ex. {'fan':{'0':'0', '2':'1'}, 'sfp':{'11':'0'}} + indicates that fan 0 has been removed, fan 2 + has been inserted and sfp 11 has been removed. + """ + # Initialize SFP event first + if not self.sfp_event_initialized: + self.sfp_event = SfpEvent() + self.sfp_event.initialize() + self.MAX_SELECT_EVENT_RETURNED = self.PORT_END + self.sfp_event_initialized = True + + wait_for_ever = (timeout == 0) + port_dict = {} + if wait_for_ever: + # xrcvd will call this monitor loop in the "SYSTEM_READY" state + # logger.log_info(" wait_for_ever get_change_event %d" % timeout) + timeout = MAX_SELECT_DELAY + while True: + status = self.sfp_event.check_sfp_status(port_dict, timeout) + if not port_dict == {}: + break + else: + # At boot up and in "INIT" state call from xrcvd will have timeout + # value return true without change after timeout and will + # transition to "SYSTEM_READY" + status = self.sfp_event.check_sfp_status(port_dict, timeout) + + if status: + return True, {'sfp': port_dict} + else: + return True, {'sfp': {}} + + def get_num_psus(self): + """ + Retrieves the num of the psus + Returns: + int: The num of the psus + """ + return PSU_NUM + + 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 + Returns: + string: Serial number of chassis + """ + return self._eeprom.serial_number_str() + + def get_status(self): + """ + Retrieves the operational status of the chassis + Returns: + bool: A boolean value, True if chassis is operating properly + False if not + """ + return True + + def get_base_mac(self): + """ + Retrieves the base MAC address for the chassis + + Returns: + A string containing the MAC address in the format + 'XX:XX:XX:XX:XX:XX' + """ + return self._eeprom.base_mac_addr() + + def get_service_tag(self): + """ + Retrieves the Service Tag of the chassis + Returns: + string: Service Tag of chassis + """ + return self._eeprom.service_tag_str() + + def get_revision(self): + """ + Retrieves the hardware revision of the chassis + + Returns: + string: Revision value of chassis + """ + #Revision is always 0 for 7250-IXR-X4 + return str(0) + + 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_thermal_manager(self): + """ + Get thermal manager + + Returns: + ThermalManager + """ + from .thermal_manager import ThermalManager + return ThermalManager + + def initizalize_system_led(self): + """ + Initizalize system led + + Returns: + bool: True if it is successful. + """ + return True + + def _find_software_reboot_cause_from_reboot_cause_file(self): + REBOOT_CAUSE_UNKNOWN = "Unknown" + REBOOT_CAUSE_DIR = "/host/reboot-cause/" + REBOOT_CAUSE_FILE = os.path.join(REBOOT_CAUSE_DIR, "reboot-cause.txt") + + software_reboot_cause = REBOOT_CAUSE_UNKNOWN + if os.path.isfile(REBOOT_CAUSE_FILE): + with open(REBOOT_CAUSE_FILE) as cause_file: + software_reboot_cause = cause_file.readline().rstrip('\n') + return software_reboot_cause + + 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. + """ + CAUSE_FORMAT = "User issued '{}' command [User: {}, Time: {}]" + CAUSE_FORMAT_NOTIME = "User issued '{}' command [User: {}]" + POWER_CAUSE_FILE = "/var/run/sonic-platform-nokia/pcon_reboot_reason" + PLATFORM_FIRSTBOOT_FLAG = "/tmp/notify_firstboot_to_platform" + + try: + with open(POWER_CAUSE_FILE) as pcon_cause_file: + pcon_cause_lines = pcon_cause_file.readlines() + + reboot_type = pcon_cause_lines[0].strip() + outtime = "N/A" + if (len(pcon_cause_lines) > 1): + timestamp = pcon_cause_lines[1].strip() + dateobj = datetime.strptime(timestamp, "%a %b %d %H:%M:%S %Y").replace( + tzinfo=timezone.utc) + outtime = dateobj.strftime("%a %b %d %I:%M:%S %p %Z %Y") + + reboot_cause = self._find_software_reboot_cause_from_reboot_cause_file() + if reboot_type == "Power Loss": + # In case of firsttime upgraded from MFG image, the PCON record + # has not been cleared, the reboto-cause should use fromreboot-cause.txt + if "reboot" in reboot_cause and os.path.exists(PLATFORM_FIRSTBOOT_FLAG): + return (self.REBOOT_CAUSE_NON_HARDWARE, None) + return (self.REBOOT_CAUSE_HARDWARE_OTHER, + CAUSE_FORMAT.format(reboot_type, "Unknown", outtime)) + else: + if "Unknown" in reboot_cause: + return (self.REBOOT_CAUSE_HARDWARE_OTHER, + CAUSE_FORMAT.format("Unknown (watchdog or others)", "Unknown", outtime)) + except Exception: + sonic_logger.log_warning("Failed to parse platform reboot-cause file") + return (self.REBOOT_CAUSE_NON_HARDWARE, None) + + return (self.REBOOT_CAUSE_NON_HARDWARE, None) + + def get_watchdog(self): + """ + Retrieves hardware watchdog device on this chassis + + Returns: + An object derived from WatchdogBase representing the hardware + watchdog device + + Note: + We overload this method to ensure that watchdog is only initialized + when it is referenced. Currently, only one daemon can open the + watchdog. To initialize watchdog in the constructor causes multiple + daemon try opening watchdog when loading and constructing a chassis + object and fail. By doing so we can eliminate that risk. + """ + try: + if self._watchdog is None: + from sonic_platform.watchdog import WatchdogImplBase + self._watchdog = WatchdogImplBase() + except Exception as e: + sonic_logger.log_warning(" Fail to load watchdog {}".format(repr(e))) + + return self._watchdog + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device. If the agent + cannot determine the parent-relative position + for some reason, or if the associated value of entPhysicalContainedIn is '0', + then the value '-1' is returned + Returns: + integer: The 1-based relative physical position in parent device or -1 if + cannot determine the position + """ + return -1 + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False + + def set_status_led(self, color): + """ + Sets the state of the 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 self.system_led_supported_color: + return False + + color_to_value = { + 'off': '0x0', + 'green': '0x6400', + 'amber': '0x88c700', + 'blue': '0x64', + 'green_blink': '0x4f006400', + 'amber_blink': '0x4f88c700' + } + value = color_to_value.get(color) + + try: + if color == 'green_blink' or color == 'amber_blink': + write_sysfs_file(REG_DIR + 'led_sys', '0x0') + write_sysfs_file(REG_DIR + 'led_sys', value) + self._sys_led = color + return True + except ValueError: + return False + + 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. + """ + return self._sys_led diff --git a/ixr7250x4/sonic_platform/component.py b/ixr7250x4/sonic_platform/component.py new file mode 100644 index 0000000..f4d395a --- /dev/null +++ b/ixr7250x4/sonic_platform/component.py @@ -0,0 +1,200 @@ +""" + NOKIA 7250 IXR-X + + Module contains an implementation of SONiC Platform Base API and + provides the Components' (e.g., BIOS, CPLD, FPGA, etc.) available in + the platform +""" + +try: + import os + import subprocess + import ntpath + import fcntl + import ctypes + import time + from sonic_platform_base.component_base import ComponentBase + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +COMPONENT_NUM = 3 +BIOS_VERSION_PATH = "/sys/class/dmi/id/bios_version" +SYSFS_DIR = [" ", + "/sys/bus/pci/devices/0000:01:00.0/", + "/sys/bus/pci/devices/0000:05:00.0/"] + +class Component(ComponentBase): + """Nokia platform-specific Component class""" + + CHASSIS_COMPONENTS = [ + ["BIOS", "Basic Input/Output System"], + ["CpuCtlFpga", "Used for managing CPM"], + ["IoCtlFpga", "Used for managing IMM"]] + + BIOS_UPDATE_COMMAND = ['./afulnx_64', '', '/P', '/B', '/N', '/K'] + FPGA_UPDATE_COMMAND = ['', '-c', '1', '-p', '', ''] + + def __init__(self, component_index): + self.index = component_index + self.name = self.CHASSIS_COMPONENTS[self.index][0] + self.description = self.CHASSIS_COMPONENTS[self.index][1] + self.sysfs_dir = SYSFS_DIR[self.index] + + def _get_command_result(self, cmdline): + try: + proc = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout = proc.communicate()[0] + proc.wait() + result = stdout.rstrip('\n') + except OSError: + result = None + + return result + + def _get_cpld_version(self): + if self.name == "BIOS": + return read_sysfs_file(BIOS_VERSION_PATH) + else: + return read_sysfs_file(self.sysfs_dir + "code_ver") + + def get_name(self): + """ + Retrieves the name of the component + + Returns: + A string containing the name of the component + """ + return self.name + + 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_presence(self): + """ + Retrieves the presence of the component + Returns: + bool: True if present, False if not + """ + return True + + 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 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._get_cpld_version() + + def install_firmware(self, image_path): + """ + Installs firmware to the component + + Args: + image_path: A string, path to firmware image + + Returns: + A boolean, True if install was successful, False if not + """ + image_name = ntpath.basename(image_path) + + # check whether the image file exists + os.chdir("/tmp") + if not os.path.isfile(image_name): + print(f"ERROR: the image {image_name} doesn't exist in /tmp") + return False + + if self.name == "BIOS": + # check whether the BIOS upgrade tool exists + if not os.path.isfile('/tmp/afulnx_64'): + print("ERROR: the BIOS upgrade tool /tmp/afulnx_64 doesn't exist ") + return False + self.BIOS_UPDATE_COMMAND[1] = image_name + try: + subprocess.run(self.BIOS_UPDATE_COMMAND, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print(f"ERROR: Failed to upgrade BIOS: rc={e.returncode}") + print("\nBIOS upgraded!\n") + return True + + return False + + 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 + + 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" + diff --git a/ixr7250x4/sonic_platform/eeprom.py b/ixr7250x4/sonic_platform/eeprom.py new file mode 100644 index 0000000..ad6b7d1 --- /dev/null +++ b/ixr7250x4/sonic_platform/eeprom.py @@ -0,0 +1,188 @@ +""" + NOKIA 7250 IXR-X + + Module contains platform specific implementation of SONiC Platform + Base API and provides the EEPROMs' information. + + The different EEPROMs available are as follows: + - System EEPROM : Contains Serial number, Service tag, Base MA + address, etc. in ONIE TlvInfo EEPROM format. +""" + +try: + from sonic_platform_base.sonic_eeprom.eeprom_tlvinfo import TlvInfoDecoder + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +sonic_logger = logger.Logger('eeprom') +sonic_logger.set_min_log_priority_info() + +class Eeprom(TlvInfoDecoder): + """Nokia platform-specific EEPROM class""" + + I2C_DIR = "/sys/bus/i2c/devices/" + + def __init__(self, is_psu, psu_index, is_fan, drawer_index): + self.is_psu_eeprom = is_psu + self.is_fan_eeprom = is_fan + self.is_sys_eeprom = not (is_psu | is_fan) + self.service_tag = 'NA' + self.part_number = 'NA' + + if self.is_sys_eeprom: + self.start_offset = 4096 + self.eeprom_path = self.I2C_DIR + "1-0054/eeprom" + # System EEPROM is in ONIE TlvInfo EEPROM format + super(Eeprom, self).__init__(self.eeprom_path, self.start_offset, '', True) + self.base_mac = '' + self.serial_number = '' + self.part_number = '' + self.model_str = '' + self.service_tag = '' + self.eeprom_tlv_dict = '' + else: + self.serial_number = 'N/A' + self.part_number = 'N/A' + self.model_str = 'N/A' + + def _load_system_eeprom(self): + """ + Reads the system EEPROM and retrieves the values corresponding + to the codes defined as per ONIE TlvInfo EEPROM format and fills + them in a dictionary. + """ + try: + # Read System EEPROM as per ONIE TlvInfo EEPROM format. + self.eeprom_data = self.read_eeprom() + except Exception as e: + sonic_logger.log_warning("Unable to read system eeprom") + self.base_mac = 'NA' + self.serial_number = 'NA' + self.part_number = 'NA' + self.model_str = 'NA' + self.service_tag = 'NA' + self.eeprom_tlv_dict = dict() + else: + eeprom = self.eeprom_data + self.eeprom_tlv_dict = dict() + + if not self.is_valid_tlvinfo_header(eeprom): + sonic_logger.log_warning("Invalid system eeprom TLV header") + self.base_mac = 'NA' + self.serial_number = 'NA' + self.part_number = 'NA' + self.model_str = 'NA' + self.service_tag = 'NA' + 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 + + self.base_mac = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_MAC_BASE), 'NA') + self.serial_number = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_SERIAL_NUMBER), 'NA') + self.part_number = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_PART_NUMBER), 'NA') + self.model_str = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_PRODUCT_NAME), 'NA') + self.service_tag = self.eeprom_tlv_dict.get( + "0x%X" % (self._TLV_CODE_SERVICE_TAG), 'NA') + sonic_logger.log_info(f"serial_number:{self.serial_number}") + + def _get_eeprom_field(self, field_name): + """ + For a field name specified in the EEPROM format, returns the + presence of the field and the value for the same. + """ + field_start = 0 + for field in self.format: + field_end = field_start + field[2] + if field[0] == field_name: + return (True, self.eeprom_data[field_start:field_end]) + field_start = field_end + + return (False, None) + + def serial_number_str(self): + """ + Returns the serial number. + """ + if not self.serial_number: + self._load_system_eeprom() + + return self.serial_number + + def part_number_str(self): + """ + Returns the part number. + """ + if not self.part_number: + self._load_system_eeprom() + + return self.part_number + + def airflow_fan_type(self): + """ + Returns the airflow fan type. + """ + if self.is_psu_eeprom: + return int(self.psu_type.encode('hex'), 16) + if self.is_fan_eeprom: + return int(self.fan_type.encode('hex'), 16) + + def base_mac_addr(self): + """ + Returns the base MAC address found in the system EEPROM. + """ + if not self.base_mac: + self._load_system_eeprom() + + return self.base_mac + + def modelstr(self): + """ + Returns the Model name. + """ + if not self.model_str: + self._load_system_eeprom() + + return self.model_str + + def service_tag_str(self): + """ + Returns the servicetag number. + """ + if not self.service_tag: + self._load_system_eeprom() + + return self.service_tag + + 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. + """ + if not self.eeprom_tlv_dict: + self._load_system_eeprom() + + return self.eeprom_tlv_dict diff --git a/ixr7250x4/sonic_platform/fan.py b/ixr7250x4/sonic_platform/fan.py new file mode 100644 index 0000000..890f031 --- /dev/null +++ b/ixr7250x4/sonic_platform/fan.py @@ -0,0 +1,333 @@ +""" + NOKIA 7250 IXR-X + + Module contains an implementation of SONiC Platform Base API and + provides the Fans' information which are available in the platform +""" + +try: + import time + import glob + from sonic_platform_base.fan_base import FanBase + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +FANS_PER_DRAWER = 4 +MAX_FAN_F_SPEED = 27000 +MAX_FAN_R_SPEED = 23000 +FAN_TOLERANCE = 50 +WORKING_FAN_SPEED = 2300 + +REG_DIR = "/sys/bus/pci/devices/0000:01:00.0/" +HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/" +I2C_BUS = [11, 12, 13] +FAN_INDEX_IN_DRAWER = [1, 2, 4, 5] +CTRLOR_ARRD = '20' +EEPROM_ADDR = '54' +LED_ADDR = '60' + +sonic_logger = logger.Logger('nokia_fan') + +class Fan(FanBase): + """Nokia platform-specific Fan class""" + + def __init__(self, fan_index, drawer_index, drawer_eeprom=None, psu_fan=False, dependency=None): + self.is_psu_fan = psu_fan + i2c_bus = I2C_BUS[drawer_index] + self.i2c_dev = f"{i2c_bus}-00{CTRLOR_ARRD}" + self.fan_led_color = ['off', 'green', 'amber', 'green_blink'] + self.led_dir = f"/sys/bus/i2c/devices/{i2c_bus}-00{LED_ADDR}/" + self.eeprom_dir = f"/sys/bus/i2c/devices/{i2c_bus}-00{EEPROM_ADDR}/" + + if not self.is_psu_fan: + # Fan is 1-based in Nokia platforms + self.index = drawer_index * FANS_PER_DRAWER + fan_index + 1 + self.reg_dir = REG_DIR + self.fan_drawer = drawer_index + self.tach_index = FAN_INDEX_IN_DRAWER[fan_index] + self.fan_inited = False + + if self.tach_index == 1 or self.tach_index == 2: + self.max_fan_speed = MAX_FAN_F_SPEED + else: + self.max_fan_speed = MAX_FAN_R_SPEED + + else: + # this is a PSU Fan + self.index = fan_index + self.dependency = dependency + + def get_name(self): + """ + Retrieves the name of the Fan + + Returns: + string: The name of the Fan + """ + if not self.is_psu_fan: + return f"Fan{self.index}" + else: + return f"PSU{self.index}_Fan" + + def fan_init(self): + """ + Retrieves the presence of the Fan Unit + + Returns: + bool: True if Fan is present, False if not + """ + hwmon_path = glob.glob(HWMON_DIR.format(self.i2c_dev)) + self.set_fan_speed_reg = hwmon_path[0] + f"pwm{self.tach_index}" + self.get_fan_speed_reg = hwmon_path[0] + f"fan{self.tach_index}_input" + self.fan_speed_enable_reg = hwmon_path[0] + f"fan{self.tach_index}_enable" + self.pwm_enable_reg = hwmon_path[0] + f"pwm{self.tach_index}_enable" + + fan_speed = read_sysfs_file(self.get_fan_speed_reg) + if (fan_speed != 'ERR'): + if (int(fan_speed) > WORKING_FAN_SPEED): + self.fan_inited = True + return True + + result = write_sysfs_file(self.pwm_enable_reg, '0') + if (result == 'ERR'): + return False + time.sleep(0.1) + write_sysfs_file(self.pwm_enable_reg, '1') + time.sleep(0.1) + write_sysfs_file(self.fan_speed_enable_reg, '1') + self.fan_inited = True + return True + + def get_presence(self): + """ + Retrieves the presence of the Fan Unit + + Returns: + bool: True if Fan is present, False if not + """ + result = read_sysfs_file(self.reg_dir + f'fandraw_{self.fan_drawer+1}_prs') + if result == '0': # present + if not self.fan_inited: + self.fan_init() + + return True + + if self.fan_inited: + self.fan_inited = False + return False + + def get_model(self): + """ + Retrieves the model number of the Fan + + Returns: + string: Model number of Fan. Use part number for this. + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "part_number") + return result.strip() + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the Fan + + Returns: + string: Serial number of Fan + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "serial_number") + return result.strip() + return 'N/A' + + def get_part_number(self): + """ + Retrieves the part number of the Fan + + Returns: + string: Part number of Fan + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "part_number") + return result.strip() + return 'N/A' + + def get_service_tag(self): + """ + Retrieves the service tag of the Fan + + Returns: + string: Service Tag of Fan + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "clei") + return result.strip() + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the Fan + + Returns: + bool: True if Fan is operating properly, False if not + """ + status = False + + if not self.fan_inited: + if not self.fan_init(): + return status + + fan_speed = read_sysfs_file(self.get_fan_speed_reg) + if (fan_speed != 'ERR'): + speed_in_rpm = int(fan_speed) + if speed_in_rpm == 0: + write_sysfs_file(self.pwm_enable_reg, '0') + time.sleep(0.1) + write_sysfs_file(self.pwm_enable_reg, '1') + time.sleep(0.1) + write_sysfs_file(self.fan_speed_enable_reg, '1') + fan_speed = read_sysfs_file(self.get_fan_speed_reg) + speed_in_rpm = int(fan_speed) + target_speed = self.get_target_speed() + if ((speed_in_rpm / self.max_fan_speed) > ((target_speed - 25) / 100)) and (speed_in_rpm > WORKING_FAN_SPEED): + status = True + else: + sonic_logger.log_warning(f"!Warning: {self.get_name()} speed: {speed_in_rpm} RPM, target speed: {target_speed}%") + + return status + + def get_direction(self): + """ + Retrieves the fan airflow direction + Possible fan directions (relative to port-side of device) + Returns: + A string, either FAN_DIRECTION_INTAKE or + FAN_DIRECTION_EXHAUST depending on fan direction + """ + return self.FAN_DIRECTION_INTAKE + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + def get_speed(self): + """ + Retrieves the speed of a Front FAN in the tray in revolutions per + minute defined by 1-based index + :param index: An integer, 1-based index of the FAN to query speed + :return: integer, denoting front FAN speed + """ + if not self.fan_inited: + if not self.get_presence(): + return 0 + + if not self.get_status(): + return 0 + + fan_duty = read_sysfs_file(self.set_fan_speed_reg) + if fan_duty != 'ERR': + dutyspeed = int(fan_duty) + fan_speed = round(dutyspeed / 2.55) + return fan_speed + return 0 + + 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 + """ + return FAN_TOLERANCE + + def set_speed(self, speed): + """ + Set fan speed to expected value + Args: + speed: An integer, the percentage of full fan speed to set + fan to, in the range 0 (off) to 100 (full speed) + Returns: + bool: True if set success, False if fail. + """ + if self.is_psu_fan: + return False + if not self.fan_inited: + if not self.get_presence(): + return False + + if speed >= 20 and speed <= 100: + fan_duty_cycle = round(speed * 2.55) + elif speed >= 0 and speed < 20: + fan_duty_cycle = 0 + else: + return False + + pwm_enable = read_sysfs_file(self.pwm_enable_reg) + if pwm_enable == '0': + write_sysfs_file(self.pwm_enable_reg, '1') + rv = write_sysfs_file(self.set_fan_speed_reg, str(fan_duty_cycle)) + if (rv != 'ERR'): + return True + else: + return False + + 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. + """ + return False + + def get_status_led(self): + """ + Gets the state of the fan status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + if not self.get_presence(): + return 'N/A' + + result = read_sysfs_file(self.led_dir + 'fan_led') + val = int(result) + if val < len(self.fan_led_color): + return self.fan_led_color[val] + return 'N/A' + + def get_target_speed(self): + """ + Retrieves the target (expected) speed of the fan + + Returns: + An integer, the percentage of full fan speed, in the range 0 + (off) to 100 (full speed) + """ + if not self.fan_inited: + if not self.get_presence(): + return False + + fan_duty = read_sysfs_file(self.set_fan_speed_reg) + if fan_duty != 'ERR': + dutyspeed = int(fan_duty) + target_speed = round(dutyspeed / 2.55) + return target_speed + return 0 diff --git a/ixr7250x4/sonic_platform/fan_drawer.py b/ixr7250x4/sonic_platform/fan_drawer.py new file mode 100644 index 0000000..4cf8627 --- /dev/null +++ b/ixr7250x4/sonic_platform/fan_drawer.py @@ -0,0 +1,227 @@ +""" + NOKIA 7250 IXR-X + + Module contains an implementation of SONiC Platform Base API and + provides the Fan Drawer status which is available in the platform +""" + +try: + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from sonic_platform_base.fan_drawer_base import FanDrawerBase + from sonic_py_common import logger + import os +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +FANS_PER_DRAWER = 4 +I2C_BUS = [11, 12, 13] +FAN_INDEX_IN_DRAWER = [1, 2, 4, 5] +EEPROM_INFO = ['54', 'fan_eeprom'] +DRIVER_INFO = ['20', 'max31790_wd'] +LED_INFO = ['60', 'fan_led'] +REG_DIR = "/sys/bus/pci/devices/0000:01:00.0/" + +sonic_logger = logger.Logger('fan_drawer') +sonic_logger.set_min_log_priority_info() + +class NokiaFanDrawer(FanDrawerBase): + def __init__(self, index): + super().__init__() + self._index = index + 1 + self.reg_dir = REG_DIR + self.fan_led_color = ['off', 'green', 'amber', 'green_blink'] + + # Possible fan directions (relative to port-side of device) + self.fan_direction_intake = "intake" + self.i2c_index = I2C_BUS[index] + self.eeprom_dir = f"/sys/bus/i2c/devices/{self.i2c_index}-00{EEPROM_INFO[0]}/" + self.driver_dir = f"/sys/bus/i2c/devices/{self.i2c_index}-00{DRIVER_INFO[0]}/" + self.led_dir = f"/sys/bus/i2c/devices/{self.i2c_index}-00{LED_INFO[0]}/" + + self.new_cmd = "echo {} 0x{} > /sys/bus/i2c/devices/i2c-{}/new_device" + self.del_cmd = "echo 0x{} > /sys/bus/i2c/devices/i2c-{}/delete_device" + + def get_index(self): + """ + Retrieves the index of the Fan Drawer + Returns: + int: the Fan Drawer's index + """ + return self._index + + def get_presence(self): + """ + Retrieves the presence of the Fan Drawer + Returns: + bool: return True if the Fan Drawer is present + """ + result = read_sysfs_file(self.reg_dir + f'fandraw_{self._index}_prs') + if result == '0': # present + if not os.path.exists(self.eeprom_dir): + try: + os.system(self.new_cmd.format(EEPROM_INFO[1], EEPROM_INFO[0], self.i2c_index)) + except: + return False + if not os.path.exists(self.driver_dir): + try: + os.system(self.new_cmd.format(DRIVER_INFO[1], DRIVER_INFO[0], self.i2c_index)) + except: + return False + if not os.path.exists(self.led_dir): + try: + os.system(self.new_cmd.format(LED_INFO[1], LED_INFO[0], self.i2c_index)) + except: + return False + + if os.path.exists(self.driver_dir+'fan_wd'): + result = read_sysfs_file(f"/sys/bus/i2c/devices/{self.i2c_index}-0020/fan_wd") + if result == '3': + return True + else: + write_sysfs_file(f"/sys/bus/i2c/devices/{self.i2c_index}-0020/fan_wd", '3') + result = read_sysfs_file(f"/sys/bus/i2c/devices/{self.i2c_index}-0020/fan_wd") + if result == '3': + return True + else: + os.system(self.del_cmd.format(DRIVER_INFO[0], self.i2c_index)) + return False + else: + os.system(self.del_cmd.format(DRIVER_INFO[0], self.i2c_index)) + return False + + # not present + if os.path.exists(self.eeprom_dir): + try: + os.system(self.del_cmd.format(EEPROM_INFO[0], self.i2c_index)) + except: + return False + + return False + + def get_model(self): + """ + Retrieves the model number of the Fan Drawer + Returns: + string: Part number of Fan Drawer + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "part_number") + return result.strip() + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the Fan Drawer + Returns: + string: Serial number of Fan + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "serial_number") + return result.strip() + return 'N/A' + + def get_part_number(self): + """ + Retrieves the part number of the Fan Drawer + + Returns: + string: Part number of Fan + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "part_number") + return result.strip() + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the Fan Drawer + Returns: + bool: True if Fan is operating properly, False if not + """ + good_fan = 0 + if not self.get_presence(): + return False + for fan in self._fan_list: + if fan.get_status(): + good_fan = good_fan + 1 + + if good_fan == FANS_PER_DRAWER: + return True + return False + + def get_direction(self): + """ + Retrieves the direction of the Fan Drawer + Returns: + string: direction string + """ + return self.fan_direction_intake + + def set_status_led(self, color): + """ + Sets the state of the fan drawer status LED + + Args: + color: A string representing the color with which to set the + fan drawer status LED + + Returns: + bool: True if status LED state is set successfully, False if not + """ + if not self.get_presence(): + return False + + try: + value = self.fan_led_color.index(color) + result = write_sysfs_file(self.led_dir + 'fan_led', str(value)) + if result: + return True + return False + except ValueError: + return False + + def get_status_led(self): + """ + Gets the state of the fan drawer LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings + """ + if not self.get_presence(): + return 'N/A' + + result = read_sysfs_file(self.led_dir + 'fan_led') + val = int(result) + if val < len(self.fan_led_color): + return self.fan_led_color[val] + return 'N/A' + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + 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 + """ + return self._index + +class RealDrawer(NokiaFanDrawer): + """ + For Nokia platforms with fan drawer(s) + """ + def __init__(self, index): + super(RealDrawer, self).__init__(index) + self._name = f'drawer{self._index}' + + def get_name(self): + """ + return module name + """ + return self._name diff --git a/ixr7250x4/sonic_platform/platform.py b/ixr7250x4/sonic_platform/platform.py new file mode 100644 index 0000000..35abe5e --- /dev/null +++ b/ixr7250x4/sonic_platform/platform.py @@ -0,0 +1,21 @@ +############################################################################# +# +# 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): + """ + Nokia platform-specific class + """ + + def __init__(self): + PlatformBase.__init__(self) + self._chassis = Chassis() diff --git a/ixr7250x4/sonic_platform/psu.py b/ixr7250x4/sonic_platform/psu.py new file mode 100644 index 0000000..3a95be9 --- /dev/null +++ b/ixr7250x4/sonic_platform/psu.py @@ -0,0 +1,300 @@ +""" + NOKIA 7250 IXR-X + + Module contains an implementation of SONiC Platform Base API and + provides the PSUs' information which are available in the platform +""" + +try: + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file + from sonic_platform_base.psu_base import PsuBase + from sonic_py_common import logger + import os +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +PSU_NUM = 2 +I2C_BUS = [14, 15] +PSU_ADDR = '5b' +PSU_EEPROM_ADDR = '53' +MAX_VOLTAGE = 264 +MIN_VOLTAGE = 180 +REG_DIR = "/sys/bus/pci/devices/0000:01:00.0/" + +sonic_logger = logger.Logger('psu') +sonic_logger.set_min_log_priority_error() + +class Psu(PsuBase): + def __init__(self, psu_index): + PsuBase.__init__(self) + # PSU is 1-based in Nokia platforms + self.index = psu_index + 1 + self._fan_list = [] + self.psu_dir = f"/sys/bus/i2c/devices/{I2C_BUS[psu_index]}-00{PSU_ADDR}/" + self.eeprom_dir = f"/sys/bus/i2c/devices/{I2C_BUS[psu_index]}-00{PSU_EEPROM_ADDR}/" + self.new_cmd = f"echo psu_eeprom 0x{PSU_EEPROM_ADDR} > /sys/bus/i2c/devices/i2c-{I2C_BUS[psu_index]}/new_device" + self.del_cmd = f"echo 0x{PSU_EEPROM_ADDR} > /sys/bus/i2c/devices/i2c-{I2C_BUS[psu_index]}/delete_device" + self.psu_led_color = ['off', 'green', 'amber'] + self._master_psu_led = 'off' + self._master_psu_led_set = False + + def _get_active_psus(self): + """ + Retrieves the operational status of the PSU and + calculates number of active PSU's + + Returns: + Integer: Number of active PSU's + """ + active_psus = 0 + for i in range(PSU_NUM): + if self.get_status(): + active_psus = active_psus + 1 + + return active_psus + + def get_name(self): + """ + Retrieves the name of the device + + Returns: + string: The name of the device + """ + return f"PSU{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 + """ + result = read_sysfs_file(self.psu_dir + "psu_status") + if int(result, 10) < 0: + if os.path.exists(self.eeprom_dir): + os.system(self.del_cmd) + return False + else: + if not os.path.exists(self.eeprom_dir): + os.system(self.new_cmd) + return True + + def get_model(self): + """ + Retrieves the part number of the PSU + + Returns: + string: Part number of PSU + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "part_number") + return result.strip() + + return 'N/A' + + def get_serial(self): + """ + Retrieves the serial number of the PSU + + Returns: + string: Serial number of PSU + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "serial_number") + return result.strip() + + return 'N/A' + + def get_revision(self): + """ + Retrieves the HW revision of the PSU + + Returns: + string: HW revision of PSU + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "mfg_date") + return result.strip() + + return 'N/A' + + def get_part_number(self): + """ + Retrieves the part number of the PSU + + Returns: + string: Part number of PSU + """ + if self.get_presence(): + result = read_sysfs_file(self.eeprom_dir + "part_number") + return result.strip() + + return 'N/A' + + def get_status(self): + """ + Retrieves the operational status of the PSU + + Returns: + bool: True if PSU is operating properly, False if not + """ + result = read_sysfs_file(self.psu_dir + "psu_status") + if (int(result, 10) & 0x800) >> 11 == 0: + return True + + return False + + def get_voltage(self): + """ + Retrieves current PSU voltage output + + Returns: + A float number, the output voltage in volts, + e.g. 12.1 + """ + if self.get_presence(): + result = read_sysfs_file(self.psu_dir + "in1_input") + psu_voltage = (float(result))/1000 + else: + psu_voltage = 0.0 + + return psu_voltage + + def get_current(self): + """ + Retrieves present electric current supplied by PSU + + Returns: + A float number, the electric current in amperes, e.g 15.4 + """ + if self.get_presence(): + result = read_sysfs_file(self.psu_dir + "curr1_input") + psu_current = (float(result))/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 + """ + if self.get_presence(): + result = read_sysfs_file(self.psu_dir + "power1_input") + psu_power = (float(result))/1000000 + else: + psu_power = 0.0 + + return psu_power + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def get_voltage_high_threshold(self): + """ + Retrieves the high threshold PSU voltage output + + Returns: + A float number, the high threshold output voltage in volts, + e.g. 12.1 + """ + return MAX_VOLTAGE + + def get_voltage_low_threshold(self): + """ + Retrieves the low threshold PSU voltage output + + Returns: + A float number, the low threshold output voltage in volts, + e.g. 12.1 + """ + return MIN_VOLTAGE + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return True + + 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. + """ + return self.get_status() + + def get_status_led(self): + """ + Gets the state of the PSU status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + if self.get_presence(): + if self.get_status(): + return self.STATUS_LED_COLOR_GREEN + else: + return self.STATUS_LED_COLOR_AMBER + else: + return 'N/A' + + def set_status_led(self, color): + """ + Sets the state of the PSU status LED + Args: + color: A string representing the color with which to set the + PSU status LED + Returns: + bool: True if status LED state is set successfully, False if + not + """ + return False + + def get_status_master_led(self): + """ + Gets the state of the front panel PSU status LED + + Returns: + A string, one of the predefined STATUS_LED_COLOR_* strings. + """ + if self._master_psu_led_set: + return self._master_psu_led + else: + return 'N/A' + + def set_status_master_led(self, color): + """ + Sets the state of the front panel PSU status LED + + Returns: + bool: True if status LED state is set successfully, False if + not + """ + if color not in self.psu_led_color: + return False + + color_to_value = { + 'off': '0x0', + 'green': '0x6400', + 'amber': '0xa4c700' + } + value = color_to_value.get(color) + + status = write_sysfs_file(REG_DIR + 'led_psu', value) + if status == 'ERR': + return False + self._master_psu_led = color + self._master_psu_led_set = True + return True diff --git a/ixr7250x4/sonic_platform/sfp.py b/ixr7250x4/sonic_platform/sfp.py new file mode 100644 index 0000000..087be31 --- /dev/null +++ b/ixr7250x4/sonic_platform/sfp.py @@ -0,0 +1,215 @@ +""" + Name: sfp.py, version: 1.0 + + Description: Module contains the definitions of SFP related APIs + for NOKIA 7250 IXR-X4 platform. + + Copyright (c) 2024, Nokia + All rights reserved. +""" + +try: + import time + import sys + from sonic_platform_base.sonic_xcvr.sfp_optoe_base import SfpOptoeBase + from sonic_py_common import logger, device_info + from sonic_platform.sysfs import read_sysfs_file, write_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +PORT_NUM = 32 + +REG_DIR = "/sys/bus/pci/devices/0000:05:00.0/" + +SYSLOG_IDENTIFIER = "sfp" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) +sonic_logger.set_min_log_priority_info() + +class Sfp(SfpOptoeBase): + """ + Nokia IXR-7220 X4 Platform-specific Sfp refactor class + """ + instances = [] + + port_to_i2c_mapping = 0 + + def __init__(self, index, sfp_type, eeprom_path, port_i2c_map): + SfpOptoeBase.__init__(self) + + self.index = index + self.port_num = index + self.sfp_type = sfp_type + + self.eeprom_path = eeprom_path + self.port_to_i2c_mapping = port_i2c_map + self.name = sfp_type + '_' + str(index) + self.port_name = sfp_type + '_' + str(index-1) + + self.port_to_eeprom_mapping = {} + self.port_to_eeprom_mapping[index] = eeprom_path + self.swpld_path = REG_DIR + + self._version_info = device_info.get_sonic_version_info() + self.lastPresence = False + + Sfp.instances.append(self) + + def get_eeprom_path(self): + """ + Retrieves the eeprom path + Returns: + string: eeprom path + """ + return self.eeprom_path + + def get_presence(self): + """ + Retrieves the presence + Returns: + bool: True if is present, False if not + """ + sfpstatus = read_sysfs_file(self.swpld_path+f"port_{self.index}_prs") + + if sfpstatus == '0': + return True + + return False + + def get_name(self): + """ + Retrieves the name of the device + Returns: + string: The name of the device + """ + return self.name + + 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 this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + + return True + + def _get_error_code(self): + """ + Get error code of the SFP module + + Returns: + The error code + """ + return NotImplementedError + + def get_error_description(self): + """ + Get error description + + Args: + error_code: The error code returned by _get_error_code + + Returns: + The error description + """ + if not self.get_presence(): + error_description = self.SFP_STATUS_UNPLUGGED + else: + error_description = self.SFP_STATUS_OK + + return error_description + + def get_reset_status(self): + """ + Retrieves the reset status of SFP + Returns: + A Boolean, True if reset enabled, False if disabled + """ + if self.index <= PORT_NUM: + result = read_sysfs_file(self.swpld_path+f"port_{self.index}_rst") + if result == '0': + return True + return False + return False + + + def get_status(self): + """ + Retrieves the operational status of the device + """ + status = True + reset = self.get_reset_status() + if self.get_presence(): + if not reset: + status = True + + return status + + def reset(self): + """ + Reset SFP. + Returns: + A boolean, True if successful, False if not + """ + if not self.get_presence(): + sys.stderr.write(f"Error: Port {self.index} not inserted, could not reset it.\n\n") + return False + sonic_logger.log_info(f"Reseting port #{self.index}.") + + result1 = 'ERR' + result2 = 'ERR' + result1 = write_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod", '1') + result2 = write_sysfs_file(self.swpld_path+f"port_{self.index}_rst", '0') + time.sleep(0.5) + result2 = write_sysfs_file(self.swpld_path+f"port_{self.index}_rst", '1') + + if result1 != 'ERR' and result2 != 'ERR': + return True + + return False + + def set_lpmode(self, lpmode): + """ + Sets the lpmode (low power mode) of SFP + Args: + lpmode: A Boolean, True to enable lpmode, False to disable it + Note : + Returns: + A boolean, True if lpmode is set successfully, False if not + """ + result = 'ERR' + + if self.index <= PORT_NUM: + if lpmode: + result = write_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod", '1') + else: + result = write_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod", '0') + + if result != 'ERR': + return True + + return False + + def get_lpmode(self): + """ + Retrieves the lpmode (low power mode) status of this SFP + Returns: + A Boolean, True if lpmode is enabled, False if disabled + """ + result = 'ERR' + + if self.index <= PORT_NUM: + result = read_sysfs_file(self.swpld_path+f"port_{self.index}_lpmod") + + if result == '1': + return True + + return False diff --git a/ixr7250x4/sonic_platform/sfp_event.py b/ixr7250x4/sonic_platform/sfp_event.py new file mode 100644 index 0000000..01f0f7f --- /dev/null +++ b/ixr7250x4/sonic_platform/sfp_event.py @@ -0,0 +1,99 @@ +"""" + NOKIA 7250 IXR-X4 + + listen for the SFP change event and return to chassis. +""" + +try: + import time + from sonic_py_common import logger + from sonic_platform.sysfs import read_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +# system level event/error +EVENT_ON_ALL_SFP = '-1' +SYSTEM_NOT_READY = 'system_not_ready' +SYSTEM_READY = 'system_become_ready' +SYSTEM_FAIL = 'system_fail' + +# SFP PORT numbers +PORT_START = 1 +PORT_END = 32 + +REG_DIR = "/sys/bus/pci/devices/0000:05:00.0/" + +SYSLOG_IDENTIFIER = "sfp_event" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) + +class SfpEvent: + ''' Listen to plugin/plugout cable events ''' + + def __init__(self): + self.handle = None + self.modprs_list = [] + + def initialize(self): + # Get Transceiver status + time.sleep(5) + self.modprs_list = self._get_transceiver_status() + + def deinitialize(self): + if self.handle is None: + return + + def _get_transceiver_status(self): + port_status = [] + reg_value = read_sysfs_file(REG_DIR + "port_prs_reg1") + bin_str = f'{int(reg_value, 16):032b}' + bin_str = bin_str[::-1] + bool_list = [not bool(int(bit)) for bit in bin_str] + port_status.extend(bool_list) + + return port_status + + def check_sfp_status(self, port_change, timeout): + """ + check_sfp_status called from get_change_event, this will return correct + status of all SFP ports if there is a change in any of them + """ + start_time = time.time() + forever = False + + if timeout == 0: + forever = True + elif timeout > 0: + timeout = timeout / float(1000) # Convert to secs + else: + return False, {} + end_time = start_time + timeout + + if start_time > end_time: + return False, {} # Time wrap or possibly incorrect timeout + + while (timeout >= 0): + # Check for OIR events and return updated port_change + port_status = self._get_transceiver_status() + if port_status != self.modprs_list: + for i in range(PORT_END): + if port_status[i] != self.modprs_list[i]: + if port_status[i] == True: + port_change[i+1] = '1' + else: + port_change[i+1] = '0' + + # Update reg value + self.modprs_list = port_status + return True, port_change + + if forever: + time.sleep(1) + else: + timeout = end_time - time.time() + if timeout >= 1: + time.sleep(1) # We poll at 1 second granularity + else: + if timeout > 0: + time.sleep(timeout) + return True, {} + return False, {} diff --git a/ixr7250x4/sonic_platform/sysfs.py b/ixr7250x4/sonic_platform/sysfs.py new file mode 100644 index 0000000..4e9c55d --- /dev/null +++ b/ixr7250x4/sonic_platform/sysfs.py @@ -0,0 +1,45 @@ +""" + Nokia platform-specific sysfs class +""" + +def read_sysfs_file(sysfs_file): + """ + On successful read, returns the value read from given + reg_name and on failure returns ERR + """ + rv = 'ERR' + + try: + with open(sysfs_file, 'r', encoding='utf-8') as fd: + rv = fd.read() + fd.close() + except FileNotFoundError: + print(f"Error: {sysfs_file} doesn't exist.") + except PermissionError: + print(f"Error: Permission denied when reading file {sysfs_file}.") + except IOError: + print(f"IOError: An error occurred while reading file {sysfs_file}.") + if rv != 'ERR': + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + +def write_sysfs_file(sysfs_file, value): + """ + On successful write, the value read will be written on + reg_name and on failure returns ERR + """ + rv = 'ERR' + + try: + with open(sysfs_file, 'w', encoding='utf-8') as fd: + rv = fd.write(value) + fd.close() + except FileNotFoundError: + print(f"Error: {sysfs_file} doesn't exist.") + except PermissionError: + print(f"Error: Permission denied when writing file {sysfs_file}.") + except IOError: + print(f"IOError: An error occurred while writing file {sysfs_file}.") + + return rv diff --git a/ixr7250x4/sonic_platform/test/README b/ixr7250x4/sonic_platform/test/README new file mode 100644 index 0000000..3efc8fa --- /dev/null +++ b/ixr7250x4/sonic_platform/test/README @@ -0,0 +1 @@ +This directory contains unit tests of the Platform API 2.0 diff --git a/ixr7250x4/sonic_platform/test/test-chassis.py b/ixr7250x4/sonic_platform/test/test-chassis.py new file mode 100755 index 0000000..93dfcaa --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-chassis.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +try: + import sonic_platform.platform + import sonic_platform.chassis +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +def main(): + print("-----------------") + print("Chassis Unit Test") + print("-----------------") + + chassis = sonic_platform.platform.Platform().get_chassis() + print(" Chassis name: {}".format(chassis.get_name())) + + print(" Chassis presence: {}".format(chassis.get_presence())) + + print(" Chassis model: {}".format(chassis.get_model())) + + print(" Chassis serial: {}".format(chassis.get_serial())) + + print(" Chassis revision: {}".format(chassis.get_revision())) + + print(" Chassis status: {}".format(chassis.get_status())) + + print(" Chassis base_mac: {}".format(chassis.get_base_mac())) + + print(" Chassis reboot cause: {}\n".format(chassis.get_reboot_cause())) + + print(" Chassis watchdog: {}".format(chassis.get_watchdog())) + + print(" Chassis num_components: {}".format(chassis.get_num_components())) + + print(" Chassis all_components: {}\n".format(chassis.get_all_components())) + + print(" Chassis num_modules: {}".format(chassis.get_num_modules())) + + print(" Chassis all_modules: {}\n".format(chassis.get_all_modules())) + + print(" Chassis num_fans: {}".format(chassis.get_num_fans())) + + print(" Chassis all_fans: {}\n".format(chassis.get_all_fans())) + + print(" Chassis num_psus: {}".format(chassis.get_num_psus())) + + print(" Chassis all_psus: {}\n".format(chassis.get_all_psus())) + + print(" Chassis num_thermals: {}".format(chassis.get_num_thermals())) + + print(" Chassis all_thermals: {}\n".format(chassis.get_all_thermals())) + + print(" Chassis num_sfps: {}".format(chassis.get_num_sfps())) + + print(" Chassis all_sfps: {}\n".format(chassis.get_all_sfps())) + + print(" Chassis eeprom: {}".format(chassis.get_eeprom())) + + print(" Chassis system_eeprom_info: {}\n".format(chassis.get_system_eeprom_info())) + + print(" Chassis get_status_led start : {}\n".format(chassis.get_status_led())) + chassis.set_status_led('amber') + print(" Chassis get_status_led amber: {}\n".format(chassis.get_status_led())) + chassis.set_status_led('green') + print(" Chassis get_status_led green: {}\n".format(chassis.get_status_led())) + + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-component.py b/ixr7250x4/sonic_platform/test/test-component.py new file mode 100755 index 0000000..1116cc7 --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-component.py @@ -0,0 +1,22 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("---------------------------") + print("Chassis Component Unit Test") + print("---------------------------") + + chassis = Chassis() + + for component in chassis.get_all_components(): + print(" Name: {}".format(component.get_name())) + print(" Description: {}".format(component.get_description())) + print(" FW version: {}\n".format(component.get_firmware_version())) + + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-eeprom.py b/ixr7250x4/sonic_platform/test/test-eeprom.py new file mode 100755 index 0000000..bee72e0 --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-eeprom.py @@ -0,0 +1,25 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("------------------------") + print("Chassis eeprom Unit Test") + print("------------------------") + + chassis = Chassis() + + eeprom = chassis.get_eeprom() + + print(" Model: {}, Service Tag: {}".format(eeprom.modelstr(), + eeprom. service_tag_str())) + print(" Part#: {}, Serial#: {}".format(eeprom.part_number_str(), + eeprom.serial_number_str())) + print(" Base MAC: {}".format(eeprom.base_mac_addr())) + + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-fan.py b/ixr7250x4/sonic_platform/test/test-fan.py new file mode 100755 index 0000000..03d2971 --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-fan.py @@ -0,0 +1,66 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + +def read_sysfs_file(sysfs_file): + """ + On successful read, returns the value read from given + reg_name and on failure returns ERR + """ + rv = 'ERR' + + try: + with open(sysfs_file, 'r', encoding='utf-8') as fd: + rv = fd.read() + fd.close() + except FileNotFoundError: + print(f"Error: {sysfs_file} doesn't exist.") + except PermissionError: + print(f"Error: Permission denied when reading file {sysfs_file}.") + except IOError: + print(f"IOError: An error occurred while reading file {sysfs_file}.") + if rv != 'ERR': + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + +def main(): + print("---------------------") + print("Chassis Fan Unit Test") + print("---------------------") + + chassis = Chassis() + + for fandraw in chassis.get_all_fan_drawers(): + if not fandraw.get_presence(): + print("\n Name: {} not present".format(fandraw.get_name())) + else: + print(" Name:", fandraw.get_name()) + print(" Presence: {}, Status: {}, LED: {}".format(fandraw.get_presence(), + fandraw.get_status(), + fandraw.get_status_led())) + mfg_date = read_sysfs_file(fandraw.eeprom_dir + "mfg_date") + print(" Part_number: {}, mfg_date: {}, Serial: {}".format(fandraw.get_model(), + mfg_date.strip(), + fandraw.get_serial())) + clei = read_sysfs_file(fandraw.eeprom_dir + "clei") + assembly_num = read_sysfs_file(fandraw.eeprom_dir + "assembly_num") + platforms = read_sysfs_file(fandraw.eeprom_dir + "platforms") + print(" CLEI: {}, assembly_num: {}, platforms: {}, ".format(clei.strip(), + assembly_num.strip(), + platforms.strip())) + hw_type = read_sysfs_file(fandraw.eeprom_dir + "hw_type") + hw_directives = read_sysfs_file(fandraw.eeprom_dir + "hw_directives") + print(" hw_type: {}, hw_directives: {}".format(hw_type.strip(), hw_directives.strip())) + for fan in fandraw.get_all_fans(): + fan_status = fan.get_status() + rpm = read_sysfs_file(fan.get_fan_speed_reg) + print(" {} Status: {}, Target Speed: {}%, Speed: {}%, RPM: {}".format(fan.get_name(), + fan_status, + str(fan.get_target_speed()), + str(fan.get_speed()), int(rpm))) + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-psu.py b/ixr7250x4/sonic_platform/test/test-psu.py new file mode 100755 index 0000000..436ed3f --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-psu.py @@ -0,0 +1,67 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + +def read_sysfs_file(sysfs_file): + """ + On successful read, returns the value read from given + reg_name and on failure returns ERR + """ + rv = 'ERR' + + try: + with open(sysfs_file, 'r', encoding='utf-8') as fd: + rv = fd.read() + fd.close() + except FileNotFoundError: + print(f"Error: {sysfs_file} doesn't exist.") + except PermissionError: + print(f"Error: Permission denied when reading file {sysfs_file}.") + except IOError: + print(f"IOError: An error occurred while reading file {sysfs_file}.") + if rv != 'ERR': + rv = rv.rstrip('\r\n') + rv = rv.lstrip(" ") + return rv + +def main(): + print("---------------------") + print("Chassis PSU Unit Test") + print("---------------------") + + chassis = Chassis() + + for psu in chassis.get_all_psus(): + if not psu.get_presence(): + print(" Name: {} not present".format(psu.get_name())) + else: + print(" Name:", psu.get_name()) + print(" Presence: {}, Status: {}, LED: {}".format(psu.get_presence(), + psu.get_status(), + psu.get_status_led())) + print(" Part_number: {}, Mfg_date: {}, Serial: {}".format(psu.get_part_number(), + psu.get_revision(), + psu.get_serial())) + clei = read_sysfs_file(psu.eeprom_dir + "clei") + hw_type = read_sysfs_file(psu.eeprom_dir + "hw_type") + hw_directives = read_sysfs_file(psu.eeprom_dir + "hw_directives") + print(" CLEI: {}, hw_type: {}, hw_directives: {}".format(clei.strip(), + hw_type.strip(), + hw_directives.strip())) + try: + current = psu.get_current() + except NotImplementedError: + current = "NA" + try: + power = psu.get_power() + except NotImplementedError: + power = "NA" + + print(" Output Voltage: {}, Output Current: {}, Input Power: {}\n".format(psu.get_voltage(), + current, + power)) + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-sfp.py b/ixr7250x4/sonic_platform/test/test-sfp.py new file mode 100755 index 0000000..bb780ca --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-sfp.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +try: + from sonic_platform.chassis import Chassis +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + + +def main(): + print("---------------------") + print("Chassis SFP Unit Test") + print("---------------------") + + chassis = Chassis() + + PORT_START = 1 + PORT_END = 34 + + for physical_port in range(PORT_START, PORT_END+1): + print(" ") + print(" SFP transceiver tests PORT = ", physical_port) + name = chassis.get_sfp(physical_port).get_name() + print(" SFP transceiver tests NAME = ", name) + + presence = chassis.get_sfp(physical_port).get_presence() + print("TEST 1 - sfp presence [ True ] ", physical_port, presence) + + status = chassis.get_sfp(physical_port).get_reset_status() + print("TEST 2 - sfp reset status [ False ] ", physical_port, status) + + txdisable = chassis.get_sfp(physical_port).get_tx_disable() + print("TEST 3 - sfp tx_disable [ False ] ", physical_port, txdisable) + + rxlos = chassis.get_sfp(physical_port).get_rx_los() + print("TEST 4 - sfp status rxlos [ False ] ", physical_port, rxlos) + + txfault = chassis.get_sfp(physical_port).get_tx_fault() + print("TEST 5 - sfp status txfault [ False ] ", physical_port, txfault) + + lpmode = chassis.get_sfp(physical_port).get_lpmode() + print("TEST 6 - sfp enable lpmode [ False ] ", physical_port, lpmode) + + trans_info = chassis.get_sfp(physical_port).get_transceiver_info() + print("TEST 7 - sfp transceiver info for port:", physical_port, trans_info) + + trans_status = chassis.get_sfp(physical_port).get_transceiver_bulk_status() + print("TEST 8 - sfp bulk status for port:", physical_port, trans_status) + + threshold = chassis.get_sfp(physical_port).get_transceiver_threshold_info() + print("TEST 9 - sfp bulk status for port:", physical_port, threshold) + + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-thermal.py b/ixr7250x4/sonic_platform/test/test-thermal.py new file mode 100755 index 0000000..95cc8e8 --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-thermal.py @@ -0,0 +1,50 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + +def main(): + print("-------------------------") + print("Chassis Thermal Unit Test") + print("-------------------------") + + chassis = Chassis() + + for thermal in chassis.get_all_thermals(): + if not thermal.get_presence(): + print(" Name: {} not present".format(thermal.get_name())) + else: + print(" Name:", thermal.get_name()) + print(" Presence: {}, Status: {}".format(thermal.get_presence(), + thermal.get_status())) + print(" Model: {}, Serial#: {}".format(thermal.get_model(), + thermal.get_serial())) + print(" Temperature(C): {}".format(thermal.get_temperature())) + + try: + low_thresh = thermal.get_low_threshold() + except NotImplementedError: + low_thresh = "NA" + try: + high_thresh = thermal.get_high_threshold() + except NotImplementedError: + high_thresh = "NA" + + print(" Low Threshold(C): {}, High Threshold(C): {}".format(low_thresh, + high_thresh)) + + try: + crit_low_thresh = thermal.get_low_critical_threshold() + except NotImplementedError: + crit_low_thresh = "NA" + try: + crit_high_thresh = thermal.get_high_critical_threshold() + except NotImplementedError: + crit_high_thresh = "NA" + + print(" Crit Low Threshold(C): {}, Crit High Threshold(C): {}\n".format(crit_low_thresh, + crit_high_thresh)) + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/test/test-watchdog.py b/ixr7250x4/sonic_platform/test/test-watchdog.py new file mode 100755 index 0000000..20805e0 --- /dev/null +++ b/ixr7250x4/sonic_platform/test/test-watchdog.py @@ -0,0 +1,22 @@ +#!/usr/bin/python + +from sonic_platform.chassis import Chassis + + +def main(): + print("---------------------") + print("Chassis Watchdog Test") + print("---------------------") + + chassis = Chassis() + + watchdog = chassis.get_watchdog() + + print(" Armed: {}".format(watchdog.is_armed())) + print(" Time Left: {}".format(watchdog.get_remaining_time())) + + return + + +if __name__ == '__main__': + main() diff --git a/ixr7250x4/sonic_platform/thermal.py b/ixr7250x4/sonic_platform/thermal.py new file mode 100644 index 0000000..10dc2c3 --- /dev/null +++ b/ixr7250x4/sonic_platform/thermal.py @@ -0,0 +1,258 @@ +""" + NOKIA 7250 IXR-X4 + + Module contains an implementation of SONiC Platform Base API and + provides the Thermals' information which are available in the platform +""" + +try: + import glob + from sonic_platform_base.thermal_base import ThermalBase + from sonic_py_common import logger + from swsscommon import swsscommon + from swsscommon.swsscommon import SonicV2Connector + from sonic_platform.sysfs import read_sysfs_file +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +sonic_logger = logger.Logger('thermal') +sonic_logger.set_min_log_priority_info() +THERMAL_NUM = 12 + +class Thermal(ThermalBase): + """Nokia platform-specific Thermal class""" + + HWMON_DIR = "/sys/bus/i2c/devices/{}/hwmon/hwmon*/" + I2C_DEV_LIST = ["7-001e", "19-0049", "19-004a", "19-004b", "1-0049", "0-0018", "0-0019"] + THERMAL_NAME = ["FPGA", "MB Left", "MB Right", "MB Center", "MB CPU", "DDR1", "DDR2", + "Max Port Temp.", "ASIC_DRAM0", "ASIC_DRAM1", "ASIC", "CPU"] + THRESHHOLD = [78.0, 68.0, 68.0, 68.0, 68.0, 75.0, 75.0, 71.0, 83.0, 83.0, 93.0, 88.0] + CRITICAL_THRESHHOLD = ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 103.0, 103.0, 112.0, 102.0] + + def __init__(self, thermal_index, sfps): + ThermalBase.__init__(self) + self.index = thermal_index + 1 + self.is_fan_thermal = False + self.dependency = None + self._minimum = None + self._maximum = None + self.thermal_high_threshold_file = None + + # sysfs file for crit high threshold value if supported for this sensor + self.thermal_high_crit_threshold_file = None + + if self.index == THERMAL_NUM: # CPU + self.device_path = glob.glob("/sys/devices/pci0000:00/0000:00:18.3/hwmon/hwmon*/") + self.thermal_temperature_file = self.device_path[0] + "temp1_input" + elif self.index >= THERMAL_NUM-3 and self.index <= THERMAL_NUM-1: # MAC internal sensor + self.thermal_temperature_file = None + elif self.index == THERMAL_NUM-4: # Max temperature of all optics + self.thermal_temperature_file = None + self.sfps = sfps + else: + try: + self.device_path = glob.glob(self.HWMON_DIR.format(self.I2C_DEV_LIST[self.index - 1])) + self.thermal_temperature_file = self.device_path[0] + "temp1_input" + except: + self.thermal_temperature_file = None + + def get_name(self): + """ + Retrieves the name of the thermal + + Returns: + string: The name of the thermal + """ + return self.THERMAL_NAME[self.index - 1] + + def get_presence(self): + """ + Retrieves the presence of the thermal + + Returns: + bool: True if thermal is present, False if not + """ + if self.dependency: + return self.dependency.get_presence() + 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 + """ + if self.dependency: + return self.dependency.get_status() + 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 + """ + if self.index >= THERMAL_NUM-3 and self.index <= THERMAL_NUM-1: + try: + db = SonicV2Connector() + db.connect(db.STATE_DB) + data_dict = db.get_all(db.STATE_DB, 'ASIC_TEMPERATURE_INFO') + if self.index == THERMAL_NUM-1: + thermal_temperature = float(data_dict['maximum_temperature']) + elif self.index == THERMAL_NUM-3: + thermal_temperature = float(data_dict['temperature_9']) + elif self.index == THERMAL_NUM-2: + thermal_temperature = float(data_dict['temperature_10']) + except: + thermal_temperature = 0 + elif self.index == THERMAL_NUM-4: + thermal_temperature = 0 + for sfp in self.sfps: + try: + if sfp.get_presence(): + temp = sfp.get_temperature() + else: + temp = None + except: + temp = None + if (temp is not None) and (temp > thermal_temperature): + thermal_temperature = temp + else: + thermal_temperature = read_sysfs_file(self.thermal_temperature_file) + if (thermal_temperature != 'ERR'): + thermal_temperature = float(thermal_temperature) / 1000 + if self.index == THERMAL_NUM: + thermal_temperature += 10.0 + else: + thermal_temperature = 0 + + if self._minimum is None or self._minimum > thermal_temperature: + self._minimum = thermal_temperature + if self._maximum is None or self._maximum < thermal_temperature: + self._maximum = thermal_temperature + + return float(f"{thermal_temperature:.3f}") + + 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 + """ + return self.THRESHHOLD[self.index - 1] + + 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 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 + """ + return self.CRITICAL_THRESHHOLD[self.index - 1] + + def set_high_critical_threshold(self): + """ + Sets the high_critical 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_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 + """ + return 0.0 + + 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_minimum_recorded(self): + """ + Retrieves minimum recorded temperature + """ + self.get_temperature() + return self._minimum + + def get_maximum_recorded(self): + """ + Retrieves maxmum recorded temperature + """ + self.get_temperature() + return self._maximum + + def get_position_in_parent(self): + """ + Retrieves 1-based relative physical position in parent device + Returns: + integer: The 1-based relative physical position in parent device + """ + return self.index + + def is_replaceable(self): + """ + Indicate whether this device is replaceable. + Returns: + bool: True if it is replaceable. + """ + return False diff --git a/ixr7250x4/sonic_platform/thermal_actions.py b/ixr7250x4/sonic_platform/thermal_actions.py new file mode 100644 index 0000000..7047154 --- /dev/null +++ b/ixr7250x4/sonic_platform/thermal_actions.py @@ -0,0 +1,238 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_action_base import ThermalPolicyActionBase + from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +sonic_logger = logger.Logger('thermal_actions') +sonic_logger.set_min_log_priority_warning() + +SPEED_FOR_HOT_START = 40 + +class SetFanSpeedAction(ThermalPolicyActionBase): + """ + Base thermal action class to set speed for fans + """ + # JSON field definition + JSON_FIELD_SPEED = 'speed' + JSON_FIELD_DEFAULT_SPEED = 'default_speed' + JSON_FIELD_HIGHTEMP_SPEED = 'hightemp_speed' + + def __init__(self): + """ + Constructor of SetFanSpeedAction + """ + self.default_speed = 60 + self.hightemp_speed = 100 + self.speed = self.default_speed + + def load_from_json(self, json_obj): + """ + Construct SetFanSpeedAction via JSON. JSON example: + { + "type": "fan.all.set_speed" + "speed": "100" + } + :param json_obj: A JSON object representing a SetFanSpeedAction action. + :return: + """ + if SetFanSpeedAction.JSON_FIELD_SPEED in json_obj: + speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_SPEED]) + if speed < 0 or speed > 100: + raise ValueError('SetFanSpeedAction invalid speed value {} in JSON policy file, valid value should be [0, 100]'. + format(speed)) + self.speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_SPEED)) + + @classmethod + def set_all_fan_speed(cls, thermal_info_dict, speed): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + fan_info_obj = thermal_info_dict[FanInfo.INFO_NAME] + for fandrawer in fan_info_obj.get_presence_fans(): + for fan in fandrawer.get_all_fans(): + fan.set_speed(int(speed)) + + @classmethod + def set_fan_speed_hot_start(cls, thermal_info_dict, last_drawer): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + fan_info_obj = thermal_info_dict[FanInfo.INFO_NAME] + for fandrawer in fan_info_obj.get_presence_fans(): + if fandrawer.get_index() == last_drawer: + for fan in fandrawer.get_all_fans(): + fan.set_speed(100) + else: + for fan in fandrawer.get_all_fans(): + fan.set_speed(int(SPEED_FOR_HOT_START)) + +@thermal_json_object('fan.all.set_speed') +class SetAllFanSpeedAction(SetFanSpeedAction): + """ + Action to set speed for all fans + """ + def execute(self, thermal_info_dict): + """ + Set speed for all fans + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + SetAllFanSpeedAction.set_all_fan_speed(thermal_info_dict, self.speed) + +@thermal_json_object('thermal.temp_check_and_set_all_fan_speed') +class ThermalRecoverAction(SetFanSpeedAction): + """ + Action to check thermal sensor temperature change status and set speed for all fans + """ + def load_from_json(self, json_obj): + """ + Construct ThermalRecoverAction via JSON. JSON example: + { + "type": "thermal.temp_check_and_set_all_fan_speed" + "default_speed": "25", + "threshold1_speed": "40", + "threshold2_speed": "75", + "hightemp_speed": "100" + } + :param json_obj: A JSON object representing a ThermalRecoverAction action. + :return: + """ + if SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED in json_obj: + default_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED]) + if default_speed < 0 or default_speed > 100: + raise ValueError('SetFanSpeedAction invalid default speed value {} in JSON policy file, valid value should be [0, 100]'. + format(default_speed)) + self.default_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_DEFAULT_SPEED)) + + if SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED in json_obj: + hightemp_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED]) + if hightemp_speed < 0 or hightemp_speed > 100: + raise ValueError('SetFanSpeedAction invalid hightemp speed value {} in JSON policy file, valid value should be [0, 100]'. + format(hightemp_speed)) + self.hightemp_speed = float(json_obj[SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED]) + else: + raise ValueError('SetFanSpeedAction missing mandatory field {} in JSON policy file'. + format(SetFanSpeedAction.JSON_FIELD_HIGHTEMP_SPEED)) + + sonic_logger.log_warning("ThermalRecoverAction: default: {}, hightemp: {}".format(self.default_speed, self.hightemp_speed)) + + def execute(self, thermal_info_dict): + """ + Check check thermal sensor temperature change status and set speed for all fans + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and \ + isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + thermal_info_obj = thermal_info_dict[ThermalInfo.INFO_NAME] + if not thermal_info_obj.is_skip_fan_ctl(): + fanctl_speed = thermal_info_obj.get_fanctl_speed() + last_drawer_index = thermal_info_obj.get_last_drawer_event() + if last_drawer_index != 0: + sonic_logger.log_warning(f"Last fan drawer has been inserted, executing hot start sequence.") + ThermalRecoverAction.set_fan_speed_hot_start(thermal_info_dict, last_drawer_index) + else: + if fanctl_speed != 0: + ThermalRecoverAction.set_all_fan_speed(thermal_info_dict, fanctl_speed) + else: + ThermalRecoverAction.set_all_fan_speed(thermal_info_dict, self.default_speed) + sonic_logger.log_warning(f"Wrong fanctl_speed, use default fan speed {self.default_speed}") + else: + sonic_logger.log_warning(f"Skip fan speed control for hot start sequence.") + +@thermal_json_object('switch.shutdown') +class SwitchPolicyAction(ThermalPolicyActionBase): + """ + Base class for thermal action. Once all thermal conditions in a thermal policy are matched, + all predefined thermal action will be executed. + """ + def execute(self, thermal_info_dict): + """ + Take action when thermal condition matches. For example, adjust speed of fan or shut + down the switch. + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + try: + import os + from sonic_platform.chassis import Chassis + for i in range(3): + fan_obj = Chassis().get_fan_drawer(i) + if fan_obj.get_presence(): + sonic_logger.log_warning(f"Fan {fan_obj.get_name()} speed: " + f"{fan_obj.get_fan(0).get_speed()}%, {fan_obj.get_fan(1).get_speed()}%, " + f"{fan_obj.get_fan(2).get_speed()}%, {fan_obj.get_fan(3).get_speed()}%.") + else: + sonic_logger.log_warning(f"Fan {fan_obj.get_name()} not presence.") + for i in range(2): + psu_obj = Chassis().get_psu(i) + if psu_obj.get_presence(): + sonic_logger.log_warning(f"{psu_obj.get_name()}: {psu_obj.get_voltage()}V, " + f"{psu_obj.get_current()}A, {psu_obj.get_power()}W.") + else: + sonic_logger.log_warning(f"{fan_obj.get_name()} not presence.") + except Exception as e: + sonic_logger.log_warning(" Fail to save fan and psu info {}".format(repr(e))) + + sonic_logger.log_error("Alarm for temperature critical is detected, reboot Device") + os.system('sync') + os.system('sync') + os.system('echo b > /proc/sysrq-trigger') + +@thermal_json_object('thermal_control.control') +class ControlThermalAlgoAction(ThermalPolicyActionBase): + """ + Action to control the thermal control algorithm + """ + # JSON field definition + JSON_FIELD_STATUS = 'status' + + def __init__(self): + self.status = True + + def load_from_json(self, json_obj): + """ + Construct ControlThermalAlgoAction via JSON. JSON example: + { + "type": "thermal_control.control" + "status": "true" + } + :param json_obj: A JSON object representing a ControlThermalAlgoAction action. + :return: + """ + if ControlThermalAlgoAction.JSON_FIELD_STATUS in json_obj: + status_str = json_obj[ControlThermalAlgoAction.JSON_FIELD_STATUS].lower() + if status_str == 'true': + self.status = True + elif status_str == 'false': + self.status = False + else: + raise ValueError('Invalid {} field value, please specify true of false'. + format(ControlThermalAlgoAction.JSON_FIELD_STATUS)) + else: + raise ValueError('ControlThermalAlgoAction ' + 'missing mandatory field {} in JSON policy file'. + format(ControlThermalAlgoAction.JSON_FIELD_STATUS)) + + def execute(self, thermal_info_dict): + """ + Disable thermal control algorithm + :param thermal_info_dict: A dictionary stores all thermal information. + :return: + """ + from .thermal_infos import ChassisInfo + if ChassisInfo.INFO_NAME in thermal_info_dict: + chassis_info_obj = thermal_info_dict[ChassisInfo.INFO_NAME] + chassis = chassis_info_obj.get_chassis() + thermal_manager = chassis.get_thermal_manager() + if self.status: + thermal_manager.start_thermal_control_algorithm() + else: + thermal_manager.stop_thermal_control_algorithm() diff --git a/ixr7250x4/sonic_platform/thermal_conditions.py b/ixr7250x4/sonic_platform/thermal_conditions.py new file mode 100644 index 0000000..da521a6 --- /dev/null +++ b/ixr7250x4/sonic_platform/thermal_conditions.py @@ -0,0 +1,62 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_condition_base import ThermalPolicyConditionBase + from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +class FanCondition(ThermalPolicyConditionBase): + def get_fan_info(self, thermal_info_dict): + from .thermal_infos import FanInfo + if FanInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[FanInfo.INFO_NAME], FanInfo): + return thermal_info_dict[FanInfo.INFO_NAME] + else: + return None + +@thermal_json_object('fan.any.presence') +class AllFanPresenceCondition(FanCondition): + def is_match(self, thermal_info_dict): + fan_info_obj = self.get_fan_info(thermal_info_dict) + return len(fan_info_obj.get_presence_fans()) > 0 if fan_info_obj else False + +class ThermalCondition(ThermalPolicyConditionBase): + def get_thermal_info(self, thermal_info_dict): + from .thermal_infos import ThermalInfo + if ThermalInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[ThermalInfo.INFO_NAME], ThermalInfo): + return thermal_info_dict[ThermalInfo.INFO_NAME] + else: + return None + +@thermal_json_object('thermal.over.high_critical_threshold') +class ThermalOverHighCriticalCondition(ThermalCondition): + def is_match(self, thermal_info_dict): + thermal_info_obj = self.get_thermal_info(thermal_info_dict) + if thermal_info_obj: + return thermal_info_obj.is_over_high_critical_threshold() + else: + return False + +class PsuCondition(ThermalPolicyConditionBase): + def get_psu_info(self, thermal_info_dict): + from .thermal_infos import PsuInfo + if PsuInfo.INFO_NAME in thermal_info_dict and isinstance(thermal_info_dict[PsuInfo.INFO_NAME], PsuInfo): + return thermal_info_dict[PsuInfo.INFO_NAME] + else: + return None + +@thermal_json_object('psu.any.absence') +class AnyPsuAbsenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_absence_psus()) > 0 if psu_info_obj else False + +@thermal_json_object('psu.all.absence') +class AllPsuAbsenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_presence_psus()) == 0 if psu_info_obj else False + +@thermal_json_object('psu.all.presence') +class AllPsuPresenceCondition(PsuCondition): + def is_match(self, thermal_info_dict): + psu_info_obj = self.get_psu_info(thermal_info_dict) + return len(psu_info_obj.get_absence_psus()) == 0 if psu_info_obj else False diff --git a/ixr7250x4/sonic_platform/thermal_infos.py b/ixr7250x4/sonic_platform/thermal_infos.py new file mode 100644 index 0000000..dd065b1 --- /dev/null +++ b/ixr7250x4/sonic_platform/thermal_infos.py @@ -0,0 +1,266 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_info_base import ThermalPolicyInfoBase + from sonic_platform_base.sonic_thermal_control.thermal_json_object import thermal_json_object + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +sonic_logger = logger.Logger('thermal_info') + +FM = 3 +TI = 5 + +@thermal_json_object('fan_info') +class FanInfo(ThermalPolicyInfoBase): + """ + Fan information needed by thermal policy + """ + # Fan information name + INFO_NAME = 'fan_info' + + def __init__(self): + self._absence_fans = set() + self._presence_fans = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence fans. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + for fandrawer in chassis.get_all_fan_drawers(): + fandrawer_pres = fandrawer.get_presence() + if fandrawer_pres and fandrawer not in self._presence_fans: + self._presence_fans.add(fandrawer) + self._status_changed = True + if fandrawer in self._absence_fans: + self._absence_fans.remove(fandrawer) + elif not fandrawer_pres and fandrawer not in self._absence_fans: + self._absence_fans.add(fandrawer) + self._status_changed = True + if fandrawer in self._presence_fans: + self._presence_fans.remove(fandrawer) + + def get_absence_fans(self): + """ + Retrieves absence fans + :return: A set of absence fans + """ + return self._absence_fans + + def get_presence_fans(self): + """ + Retrieves presence fans + :return: A set of presence fans + """ + return self._presence_fans + + def is_status_changed(self): + """ + Retrieves if the status of fan information changed + :return: True if status changed else False + """ + return self._status_changed + +@thermal_json_object('thermal_info') +class ThermalInfo(ThermalPolicyInfoBase): + """ + Thermal information needed by thermal policy + """ + # Fan information name + INFO_NAME = 'thermal_info' + WARNING_THRESHHOLD = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 95.0, 95.0, 95.0, 95.0, 105.0, 105.0, 0.0] + + def __init__(self): + self._old_threshold_level = -1 + self._current_threshold_level = 0 + self._num_fan_levels = 2 + self.p = 0 + self.se = 0 + self.ps = 1 + self.mc = 100 + self.nc = 38 + self._fan_drawer = 0 + self.last_drawer_insert = 0 + + def collect(self, chassis): + """ + Collect thermal sensor temperature change status + :param chassis: The chassis object + :return: + """ + self._temps = [] + self._t = [] + self._fan_speeds = [] + self._over_high_critical_threshold = False + + num_of_thermals = chassis.get_num_thermals() + for index in range(num_of_thermals): + temp_obj = chassis.get_thermal(index) + temp_current = temp_obj.get_temperature() + self._temps.insert(index, temp_current) + self._t.insert(index, temp_obj.get_high_threshold() - temp_current) + temp_crit_threshold = temp_obj.get_high_critical_threshold() + + if temp_crit_threshold != 'N/A' and temp_current >= temp_crit_threshold: + self._over_high_critical_threshold = True + sonic_logger.log_warning(f"!!!Alarm: {temp_obj.get_name()} temperature is {temp_current}C!!!") + elif self.WARNING_THRESHHOLD[index] != 0 and temp_current >= self.WARNING_THRESHHOLD[index]: + sonic_logger.log_warning(f"!Warning: {temp_obj.get_name()} temperature is {temp_current}C!") + + t_min = min(self._t) + + self.good_fan = 0 + fan_speed_sum = 0 + self.fan_drawer = 0 + for fandrawer in chassis.get_all_fan_drawers(): + if fandrawer.get_presence(): + self.fan_drawer += 1 + for fan in fandrawer.get_all_fans(): + fan_speed = fan.get_speed() + if fan_speed >= self.nc: + fan_speed_sum += fan_speed + self.good_fan += 1 + else: + self.fan_not_presence = fandrawer.get_index() + + avg_fan_speed = round(fan_speed_sum / self.good_fan) + + diff = self.fc(t_min-FM, avg_fan_speed) + if (avg_fan_speed - diff <= self.nc): + self.fanctl_speed = self.nc + elif (avg_fan_speed - diff >= self.mc): + self.fanctl_speed = self.mc + else: + self.fanctl_speed = avg_fan_speed - diff + + if self.last_drawer_insert != 0: + self._skip_fan_ctl = True + else: + self._skip_fan_ctl = False + + if self._fan_drawer == 2 and self.fan_drawer == 3: + self.last_drawer_insert = self.fan_not_presence + else: + self.last_drawer_insert = 0 + + self._fan_drawer = self.fan_drawer + + def is_over_high_critical_threshold(self): + """ + Retrieves if the temperature is over high critical threshold + :return: True if the temperature is over high critical threshold else False + """ + return self._over_high_critical_threshold + + def is_skip_fan_ctl(self): + return self._skip_fan_ctl + + def fc(self, t, f): + m = (t - self.p)/TI + + if (t == 0 or (self.p > 0 and t < 0) or (self.p < 0 and t > 0)): + self.se = 0 + self.ps = 1 + + if (t < -1): + a = 0.4 * -1 * t + else: + a = 0.4 + + if ((f > self.nc) and (f < self.mc)): + self.se += t + self.ps += 1 + + result = (a * t) + (0.002 * self.se)/self.ps + (10 * m) + + self.p = t + return round(result) + + def get_fanctl_speed(self): + if (self.fanctl_speed >= self.nc or self.fanctl_speed <= self.mc): + return self.fanctl_speed + else: + return 0 + + def get_last_drawer_event(self): + return self.last_drawer_insert + +@thermal_json_object('psu_info') +class PsuInfo(ThermalPolicyInfoBase): + """ + PSU information needed by thermal policy + """ + INFO_NAME = 'psu_info' + + def __init__(self): + self._absence_psus = set() + self._presence_psus = set() + self._status_changed = False + + def collect(self, chassis): + """ + Collect absence and presence PSUs. + :param chassis: The chassis object + :return: + """ + self._status_changed = False + for psu in chassis.get_all_psus(): + if psu.get_presence() and psu.get_powergood_status() and psu not in self._presence_psus: + self._presence_psus.add(psu) + self._status_changed = True + if psu in self._absence_psus: + self._absence_psus.remove(psu) + elif (not psu.get_presence() or not psu.get_powergood_status()) and psu not in self._absence_psus: + self._absence_psus.add(psu) + self._status_changed = True + if psu in self._presence_psus: + self._presence_psus.remove(psu) + + def get_absence_psus(self): + """ + Retrieves presence PSUs + :return: A set of absence PSUs + """ + return self._absence_psus + + def get_presence_psus(self): + """ + Retrieves presence PSUs + :return: A set of presence fans + """ + return self._presence_psus + + def is_status_changed(self): + """ + Retrieves if the status of PSU information changed + :return: True if status changed else False + """ + return self._status_changed + +@thermal_json_object('chassis_info') +class ChassisInfo(ThermalPolicyInfoBase): + """ + Chassis information needed by thermal policy + """ + INFO_NAME = 'chassis_info' + + def __init__(self): + self._chassis = None + + def collect(self, chassis): + """ + Collect platform chassis. + :param chassis: The chassis object + :return: + """ + self._chassis = chassis + + def get_chassis(self): + """ + Retrieves platform chassis object + :return: A platform chassis object. + """ + return self._chassis diff --git a/ixr7250x4/sonic_platform/thermal_manager.py b/ixr7250x4/sonic_platform/thermal_manager.py new file mode 100644 index 0000000..69d16f8 --- /dev/null +++ b/ixr7250x4/sonic_platform/thermal_manager.py @@ -0,0 +1,57 @@ +try: + from sonic_platform_base.sonic_thermal_control.thermal_manager_base import ThermalManagerBase + from .thermal_actions import * + from .thermal_conditions import * + from .thermal_infos import * +except ImportError as e: + raise ImportError(str(e) + ' - required module not found') from e + +THERMALD_INTERVAL = 5 + +class ThermalManager(ThermalManagerBase): + THERMAL_ALGORITHM_CONTROL_PATH = '/var/run/hw-management/config/suspend' + + @classmethod + def get_interval(cls): + return THERMALD_INTERVAL + + @classmethod + def start_thermal_control_algorithm(cls): + """ + Start thermal control algorithm + + Returns: + bool: True if set success, False if fail. + """ + cls._control_thermal_control_algorithm(False) + + @classmethod + def stop_thermal_control_algorithm(cls): + """ + Stop thermal control algorithm + + Returns: + bool: True if set success, False if fail. + """ + cls._control_thermal_control_algorithm(True) + + @classmethod + def _control_thermal_control_algorithm(cls, suspend): + """ + Control thermal control algorithm + + Args: + suspend: Bool, indicate suspend the algorithm or not + + Returns: + bool: True if set success, False if fail. + """ + status = True + write_value = 1 if suspend else 0 + try: + with open(cls.THERMAL_ALGORITHM_CONTROL_PATH, 'w') as control_file: + control_file.write(str(write_value)) + except (ValueError, IOError): + status = False + + return status diff --git a/ixr7250x4/sonic_platform/watchdog.py b/ixr7250x4/sonic_platform/watchdog.py new file mode 100644 index 0000000..943bba2 --- /dev/null +++ b/ixr7250x4/sonic_platform/watchdog.py @@ -0,0 +1,41 @@ +""" + Module contains an implementation of SONiC Platform Base API and + provides access to hardware watchdog +""" +try: + from sonic_platform_base.watchdog_base import WatchdogBase + from sonic_py_common import logger +except ImportError as e: + raise ImportError(str(e) + "- required module not found") + +SYSLOG_IDENTIFIER = "nokia-wd" +sonic_logger = logger.Logger(SYSLOG_IDENTIFIER) +sonic_logger.set_min_log_priority_info() + +class WatchdogImplBase(WatchdogBase): + _initialized = False + + def __init__(self): + super(WatchdogImplBase, self).__init__() + sonic_logger.log_info("Watchdog __init__{}".format(self)) + + def arm(self, seconds): + self._initialized = True + sonic_logger.log_info("Watchdog arm") + # hook this up to actual kicker shortly + return seconds + + def disarm(self): + sonic_logger.log_info("Watchdog disarm") + return False + + def is_armed(self): + if self._initialized is True: + sonic_logger.log_info("Watchdog is_armed") + return True + else: + return False + + def get_remaining_time(self): + sonic_logger.log_info("Watchdog get_remaining_time") + return 30