meta-facebook/recipes-utils/pem/files/pem.c (1,400 lines of code) (raw):

/* * Copyright 2019-present Facebook. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <signal.h> #include <assert.h> #include <libgen.h> #include <openbmc/kv.h> #include <openbmc/log.h> #include <openbmc/obmc-i2c.h> #include <openbmc/obmc-pmbus.h> #include <openbmc/pal.h> #include <openbmc/fruid.h> #include <facebook/wedge_eeprom.h> #include "pem.h" #include "pem-platform.h" static delta_hdr_t delta_hdr; static int g_fd = -1; static void exithandler(int signum) { printf("\nPEM update abort!\n"); syslog(LOG_WARNING, "PEM update abort!"); close(g_fd); run_command("rm /var/run/pem-util.pid"); exit(0); } smbus_info_t smbus_ltc4282[] = { {"CONTROL", 0x00, 2}, {"ALERT", 0x02, 2}, {"FAULT_LOG", 0x04, 1}, {"ADC_ALERT_LOG", 0x05, 1}, {"FET_BAD_FAULT_TIME", 0x06, 1}, {"GPIO_CONGIG", 0x07, 1}, {"VGPIO_ALARM_MIN", 0x08, 1}, {"VGPIO_ALARM_MAX", 0x09, 1}, {"VSOURCE_ALARM_MIN", 0x0A, 1}, {"VSOURCE_ALARM_MAX", 0x0B, 1}, {"VSENSE_ALARM_MIN", 0x0C, 1}, {"VSENSE_ALARM_MAX", 0x0D, 1}, {"POWER_ALARM_MIN", 0x0E, 1}, {"POWER_ALARM_MAX", 0x0F, 1}, {"CLOCK_DIVIDER", 0x10, 1}, {"ILIM_ADJUST", 0x11, 1}, {"ENERGY", 0x12, 6}, {"TIME_COUNTER", 0x18, 4}, {"ALERT_CONTROL", 0x1C, 1}, {"ADC_CONTROL", 0x1D, 1}, {"STATUS", 0x1E, 2}, {"EE_CONTROL", 0x20, 2}, {"EE_ALERT", 0x22, 2}, {"EE_FAULT_LOG", 0x24, 1}, {"EE_ADC_ALERT_LOG", 0x25, 1}, {"EE_FET_BAD_FAULT_TIME", 0x26, 1}, {"EE_GPIO_CONFIG", 0x27, 1}, {"EE_VGPIO_ALARM_MIN", 0x28, 1}, {"EE_VGPIO_ALARM_MAX", 0x29, 1}, {"EE_VSOURCE_ALARM_MIN", 0x2A, 1}, {"EE_VSOURCE_ALARM_MAX", 0x2B, 1}, {"EE_VSENSE_ALARM_MIN", 0x2C, 1}, {"EE_VSENSE_ALARM_MAX", 0x2D, 1}, {"EE_POWER_ALARM_MIN", 0x2E, 1}, {"EE_POWER_ALARM_MAX", 0x2F, 1}, {"EE_CLOCK_DECIMATOR", 0x30, 1}, {"EE_ILIM_ADJUST", 0x31, 1}, {"VGPIO", 0x34, 2}, {"VGPIO_MIN", 0x36, 2}, {"VGPIO_MAX", 0x38, 2}, {"VIN", 0x3A, 2}, {"VOUT", 0x3A, 2}, {"VSOURCE_MIN", 0x3C, 2}, {"VSOURCE_MAX", 0x3E, 2}, {"VSENSE", 0x40, 2}, {"VSENSE_MIN", 0x42, 2}, {"VSENSE_MAX", 0x44, 2}, {"POWER", 0x46, 2}, {"POWER_MIN", 0x48, 2}, {"POWER_MAX", 0x4A, 2}, {"EE_SCRATCH", 0x4C, 4}, {"APP_FW_MAJOR", 0x50, 1}, {"APP_FW_MINOR", 0x51, 1}, {"BL_FW_MAJOR", 0x52, 1}, {"BL_FW_MINOR", 0x53, 1}, }; pem_eeprom_reg_t pem_default_config = { .control.reg_val.value = 0x3f02, .alert.reg_val.value = 0, .fault.reg_val.value = 0, .adc_alert_log.reg_val.value = 0, .fet_bad_fault_time = 0xff, .gpio_config.reg_val.value = 0x40, .vgpio_alarm_min = 0, .vgpio_alarm_max = 0xff, .vsource_alarm_min = 0, .vsource_alarm_max = 0xff, .vsense_alarm_min = 0, .vsense_alarm_max = 0xff, .power_alarm_min = 0, .power_alarm_max = 0xff, .clock_decimator.reg_val.value = 0x08, .ilim_adjust.reg_val.value = 0x55, }; static void sensord_operation(uint8_t num, uint8_t action) { if (action == STOP) { syslog(LOG_WARNING, "Stop monitor PEM%d sensor to update", num + 1); run_command("sv stop sensord > /dev/null"); switch (num) { case 0: run_command("/usr/local/bin/sensord scm smb pem1 > /dev/null 2>&1 &"); break; case 1: run_command("/usr/local/bin/sensord scm smb pem2> /dev/null 2>&1 &"); break; case 2: run_command("/usr/local/bin/sensord scm smb pem3 > /dev/null 2>&1 &"); break; case 3: run_command("/usr/local/bin/sensord scm smb pem4> /dev/null 2>&1 &"); break; } } else if (action == START) { run_command("killall sensord"); run_command("sv start sensord > /dev/nul"); syslog(LOG_WARNING, "Start monitor PEM%d sensor", num + 1); } } /* * for reading ltc4282 registers */ static int pem_reg_read(int fd, uint8_t reg, void *value) { uint8_t byte; uint16_t word = 0; if (fd <= 0 || value == NULL) return -1; switch (reg) { case ENERGY: case TIME_COUNTER: case EE_SCRATCH: return -1; case VGPIO: byte = i2c_smbus_read_byte_data(fd, smbus_ltc4282[ILIM_ADJUST].reg); /* Select GPIO3 */ byte &= (~0x03); byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[ILIM_ADJUST].reg, byte); msleep(100); word = i2c_smbus_read_word_data(fd, smbus_ltc4282[reg].reg); *(uint16_t *)value = ((uint16_t)(word << 8) | word >> 8); break; case VIN: byte = i2c_smbus_read_byte_data(fd, smbus_ltc4282[ILIM_ADJUST].reg); /* Select Vin, Enable 12-bits mode */ byte |= 0x05; byte &= (~0x03); byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[ILIM_ADJUST].reg, byte); msleep(100); word = i2c_smbus_read_word_data(fd, smbus_ltc4282[reg].reg); *(uint16_t *)value = ((uint16_t)(word << 8) | word >> 8); break; case VOUT: byte = i2c_smbus_read_byte_data(fd, smbus_ltc4282[EE_ILIM_ADJUST].reg); /* Select Vout, Enable 12-bits mode */ byte |= 0x01; byte &= (~0x05); byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[ILIM_ADJUST].reg, byte); msleep(100); word = i2c_smbus_read_word_data(fd, smbus_ltc4282[reg].reg); *(uint16_t *)value = ((uint16_t)(word << 8) | word >> 8); break; case CONTROL: case ALERT: case STATUS: case VGPIO_MIN: case VGPIO_MAX: case VSOURCE_MIN: case VSOURCE_MAX: case VSENSE: case VSENSE_MIN: case VSENSE_MAX: case POWER: case POWER_MIN: case POWER_MAX: case EE_CONTROL: case EE_ALERT: word = i2c_smbus_read_word_data(fd, smbus_ltc4282[reg].reg); *(uint16_t *)value = ((uint16_t)(word << 8) | word >> 8); break; case FAULT_LOG: case ADC_ALERT_LOG: case FET_BAD_FAULT_TIME: case GPIO_CONGIG: case VGPIO_ALARM_MIN: case VGPIO_ALARM_MAX: case VSOURCE_ALARM_MIN: case VSOURCE_ALARM_MAX: case VSENSE_ALARM_MIN: case VSENSE_ALARM_MAX: case POWER_ALARM_MIN: case POWER_ALARM_MAX: case CLOCK_DIVIDER: case ILIM_ADJUST: case ALERT_CONTROL: case ADC_CONTROL: case EE_FAULT: case EE_ADC_ALERT_LOG: case EE_FET_BAD_FAULT_TIME: case EE_GPIO_CONFIG: case EE_VGPIO_ALARM_MIN: case EE_VGPIO_ALARM_MAX: case EE_VSOURCE_ALARM_MIN: case EE_VSOURCE_ALARM_MAX: case EE_VSENSE_ALARM_MIN: case EE_VSENSE_ALARM_MAX: case EE_POWER_ALARM_MIN: case EE_POWER_ALARM_MAX: case EE_CLOCK_DECIMATOR: case EE_ILIM_ADJUST: case APP_FW_MAJOR: case APP_FW_MINOR: case BL_FW_MAJOR: case BL_FW_MINOR: byte = i2c_smbus_read_byte_data(fd, smbus_ltc4282[reg].reg); *(uint8_t *)value = byte; break; default: return -1; break; } return 0; } /* * if write ltc4282 eeprom regs, check if write done */ static int wait_eeprom_done(int fd) { pem_status_t status; int retry = 10; if(fd <= 0) return -1; while(retry--) { if(pem_reg_read(fd, STATUS, &status.reg_val.value) == -1) return -1; if(status.reg_val.values.eeprom_busy == 0) { break; } msleep(100); } if(retry == 0) return -1; return 0; } /* * for writing ltc4282 registers */ static int pem_reg_write(int fd, uint8_t reg, int value) { uint8_t byte; uint16_t word; if (fd <= 0) return -1; switch (reg) { case ENERGY: case TIME_COUNTER: case EE_SCRATCH: case VIN: case VOUT: case VGPIO: case VSENSE: case POWER: case STATUS: break; case CONTROL: case ALERT: case VGPIO_MIN: case VGPIO_MAX: case VSOURCE_MIN: case VSOURCE_MAX: case VSENSE_MIN: case VSENSE_MAX: case POWER_MIN: case POWER_MAX: word = value & 0xFFFF; word = i2c_smbus_write_word_data(fd, smbus_ltc4282[reg].reg, word); break; case EE_CONTROL: case EE_ALERT: /* ltc4282 eeprom not support word write */ word = value & 0xFFFF; if(wait_eeprom_done(fd) == -1) return -1; byte = (word & 0xFF00) >> 8; byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[reg].reg, byte); if(wait_eeprom_done(fd) == -1) return -1; byte = word & 0xFF; byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[reg].reg + 1, byte); break; case FAULT_LOG: case ADC_ALERT_LOG: case FET_BAD_FAULT_TIME: case GPIO_CONGIG: case VGPIO_ALARM_MIN: case VGPIO_ALARM_MAX: case VSOURCE_ALARM_MIN: case VSOURCE_ALARM_MAX: case VSENSE_ALARM_MIN: case VSENSE_ALARM_MAX: case POWER_ALARM_MIN: case POWER_ALARM_MAX: case CLOCK_DIVIDER: case ILIM_ADJUST: case ALERT_CONTROL: case ADC_CONTROL: byte = value & 0xFF; byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[reg].reg, byte); break; case EE_FAULT: case EE_ADC_ALERT_LOG: case EE_FET_BAD_FAULT_TIME: case EE_GPIO_CONFIG: case EE_VGPIO_ALARM_MIN: case EE_VGPIO_ALARM_MAX: case EE_VSOURCE_ALARM_MIN: case EE_VSOURCE_ALARM_MAX: case EE_VSENSE_ALARM_MIN: case EE_VSENSE_ALARM_MAX: case EE_POWER_ALARM_MIN: case EE_POWER_ALARM_MAX: case EE_CLOCK_DECIMATOR: case EE_ILIM_ADJUST: if(wait_eeprom_done(fd) == -1) return -1; byte = value & 0xFF; byte = i2c_smbus_write_byte_data(fd, smbus_ltc4282[reg].reg, byte); break; default: break; } return 0; } /* * ltc4282 Vin/Vout 16 bits adc convert to Volts * refer to ltc4282 datasheet page 23 */ static inline float ltc4282_vsourve_calculate(int reg_val) { return (reg_val * 16.64 / 65535); } /* * ltc4282 Current 16 bits adc convert to Amps * refer to ltc4282 datasheet page 23 * Rsense = 0.0001875 ohm */ static inline float ltc4282_vsense_calculate(int reg_val) { return (reg_val * 0.04 / 65535 / 0.0001875); } /* * ltc4282 Power 16 bits adc convert to Watts * refer to ltc4282 datasheet page 23 * Rsense = 0.0001875 ohm */ static inline float ltc4282_power_calculate(int reg_val) { return (reg_val * 16.64 * 0.04 / 65535 / 0.0001875); } /* * ltc4282 Hot Swap Temp 16 bits adc convert to degree C * linear correction */ static inline float ltc4282_temp_calculate(int reg_val) { float temp = ((reg_val * 14412.0 - 15000000) / 10000) / 1000; return temp > 0 ? temp : 0; } /* * From datesheet reg addr 0x1E & 0x1F, * the temps LSBs(decimal part) are three bits. * So the unit is 1/8C, need to multiply by 125 = 1/8*1000. */ static inline float max6615_temp_calculate(int reg_val) { return reg_val * 0.125; } /* * formula come from recalibration */ static inline float max6615_fan_calculate(int reg_val) { return ((reg_val == 0xff || reg_val == 0) ? 0 : 57600.0 / reg_val); } /* * refer to MAX6615 datasheet page 13th */ static inline float max6615_pwm_calculate(int reg_val) { return reg_val / 240.0 * 100; } int is_pem_prsnt(uint8_t num, uint8_t *status) { return pal_is_fru_prsnt(num + FRU_PEM1, status); } static void mkdir_recurse(const char *dir, mode_t mode) { if (access(dir, F_OK) == -1) { char curr[MAX_KEY_PATH_LEN]; char parent[MAX_KEY_PATH_LEN]; strcpy(curr, dir); strcpy(parent, dirname(curr)); mkdir_recurse(parent, mode); mkdir(dir, mode); } } /* * file_write * value is pointer of write buffer * len is the size of value. If 0, it is assumed value is * a string and strlen() is used to determine the length. * flags is bitmask of options. * * return 0 on success, negative error code on failure. */ int file_write(const char *file, char *value, size_t len, unsigned int flags) { FILE *fp; int rc, ret = -1; bool present = true; char *curr_value; /* If the key already exists, and user wants to create it, * deny the access */ if ((flags & KV_FCREATE) && access(file, F_OK) != -1) { return -1; } /* Optimizes a lot of things */ if (len == 0) { len = strlen(value); } curr_value = malloc(len); if(curr_value == NULL) { return -1; } memset(curr_value, 0, len); fp = fopen(file, "rb+"); if (!fp && (errno == ENOENT)) { fp = fopen(file, "wb"); present = false; } if (!fp) { ret = -1; goto err_exit; } rc = flock(fileno(fp), LOCK_EX); if (rc < 0) { goto close_bail; } // Check if we are writing the same value. If so, exit early // to save on number of times flash is updated. if (present && (flags && KV_FPERSIST)) { rc = (int)fread(curr_value, 1, PEM_ARCHIVE_BUFF, fp); if (len == rc && !memcmp(value, curr_value, len)) { ret = 0; goto unlock_bail; } rc = fseek(fp, 0, SEEK_SET); if (rc < 0) { goto close_bail; } } if (ftruncate(fileno(fp), 0) < 0) { //truncate cache file after getting flock goto unlock_bail; } rc = fwrite(value, 1, len, fp); if (rc < 0) { goto unlock_bail; } rc= fflush(fp); if (rc != 0) { goto unlock_bail; } if (rc != len) { goto unlock_bail; } ret = 0; unlock_bail: rc = flock(fileno(fp), LOCK_UN); if (rc < 0) { ret = -1; } close_bail: fclose(fp); err_exit: free(curr_value); return ret; } /* * file_read * value is pointer of read buffer * len is the return size of value. If NULL then this information * is not returned to the user (And assumed to be a string). * flags is bitmask of options. * * return 0 on success, negative error code on failure. */ int file_read(const char *file, char *value, size_t *len) { FILE *fp; int rc, ret=-1; fp = fopen(file, "rb"); if (!fp) { return -1; } rc = flock(fileno(fp), LOCK_EX); if (rc < 0) { goto close_bail; } rc = (int) fread(value, 1, PEM_ARCHIVE_BUFF, fp); if (rc < 0 || ferror(fp)) { goto unlock_bail; } if (len) *len = rc; ret = 0; unlock_bail: rc = flock(fileno(fp), LOCK_UN); if (rc < 0) { ret = -1; } close_bail: fclose(fp); return ret; } /* * archive ltc4282 & max6615 registers to /mnt/data/pem folder */ int archive_pem_chips(uint8_t num) { int ret = 0; char *chip_regs; char path[MAX_KEY_PATH_LEN]; for(int chip = LTC4282; chip <= MAX6615; chip++) { memset(path, 0, MAX_KEY_PATH_LEN); strcpy(path, pem[num].file_path[chip]); mkdir_recurse(dirname(path), 0777); pem[num].fd = i2c_cdev_slave_open(pem[num].bus, pem[num].chip_addr[chip], I2C_SLAVE_FORCE_CLAIM); if (pem[num].fd < 0) { ERR_PRINT("Fail to open i2c"); ret |= (1 << chip); continue; } chip_regs = malloc(pem[num].chip_reg_cnt[chip]); if(chip_regs == NULL) { close(pem[num].fd); ret |= (1 << chip); continue; } for(int reg = 0; reg < pem[num].chip_reg_cnt[chip]; reg++) { *(chip_regs + reg) = i2c_smbus_read_byte_data(pem[num].fd, reg); } file_write(pem[num].file_path[chip], chip_regs, pem[num].chip_reg_cnt[chip], KV_FPERSIST); free(chip_regs); } return ret; } static void print_pem_status_regs(pem_status_regs_t *status_regs); static int print_ltc4282_crit_sensors(char *chip_regs) { int ret = 0; int reg_val; int threshold_sensors[] = { VIN, VOUT, VSENSE, POWER, VGPIO }; pem_status_regs_t discrete_sensors; pem_status_regs_t discrete_sensors_ee; if(chip_regs == NULL) { return -1; } for (int sensor = 0; sensor < sizeof(threshold_sensors) / sizeof(int); sensor++) { int reg = threshold_sensors[sensor]; reg_val = 0; reg_val = (*(chip_regs + smbus_ltc4282[reg].reg)) << 8 | (*(chip_regs + smbus_ltc4282[reg].reg + 1)); switch(reg) { case VIN: printf("%-32s: %.2f Volts\n", "IN_VOLT", ltc4282_vsourve_calculate(reg_val)); break; case VOUT: printf("%-32s: %.2f Volts\n", "OUT_VOLT", ltc4282_vsourve_calculate(reg_val)); break; case VSENSE: printf("%-32s: %.2f Amps\n", "CURR", ltc4282_vsense_calculate(reg_val)); break; case POWER: printf("%-32s: %.2f Watts\n", "POWER", ltc4282_power_calculate(reg_val)); break; case VGPIO: printf("%-32s: %.2f C\n", "HOT_SWAP_TEMP", ltc4282_temp_calculate(reg_val)); break; } } discrete_sensors.fault.reg_val.value = *(chip_regs + smbus_ltc4282[FAULT_LOG].reg); discrete_sensors.adc_alert_log.reg_val.value = *(chip_regs + smbus_ltc4282[ADC_ALERT_LOG].reg); discrete_sensors.status.reg_val.value = (*(chip_regs + smbus_ltc4282[STATUS].reg)) << 8 | (*(chip_regs + smbus_ltc4282[STATUS].reg + 1)); printf("\nDecode ltc4282 STATUS/FAULT/ADC_ALERT_LOG:"); print_pem_status_regs(&discrete_sensors); discrete_sensors_ee.fault.reg_val.value = *(chip_regs + smbus_ltc4282[FAULT_LOG].reg); discrete_sensors_ee.adc_alert_log.reg_val.value = *(chip_regs + smbus_ltc4282[ADC_ALERT_LOG].reg); discrete_sensors_ee.status.reg_val.value = (*(chip_regs + smbus_ltc4282[STATUS].reg)) << 8 | (*(chip_regs + smbus_ltc4282[STATUS].reg + 1)); printf("\nDecode ltc4282 EE_STATUS/EE_FAULT_LOG/EE_ADC_ALERT_LOG:"); print_pem_status_regs(&discrete_sensors_ee); return ret; } static int print_max6615_crit_sensors(char *chip_regs) { int ret = 0; int temp; int fan_cnt; int pwm_target; int pwm_output; if(chip_regs == NULL) return -1; temp = *(chip_regs + MAX6615_REG_TEMP(0)) << 3 | (*(chip_regs + MAX6615_REG_TEMP_EXT(0)) >> 5); fan_cnt = *(chip_regs + MAX6615_REG_FAN_CNT(0)); pwm_target = *(chip_regs + MAX6615_REG_TARGT_PWM(0)); pwm_output = *(chip_regs + MAX6615_REG_INSTANTANEOUS_PWM(0)); printf("%-32s: %.2f C\n", "AIR_INLET_TEMP", max6615_temp_calculate(temp)); printf("%-32s: %.2f RPM\n", "FAN1_SPEED", max6615_fan_calculate(fan_cnt)); printf("%-32s: %.2f %%\n", "FAN1_TARGET_PWM", max6615_pwm_calculate(pwm_target)); printf("%-32s: %.2f %%\n", "FAN1_PWM_OUTPUT", max6615_pwm_calculate(pwm_output)); temp = *(chip_regs + MAX6615_REG_TEMP(1)) << 3 | (*(chip_regs + MAX6615_REG_TEMP_EXT(1)) >> 5); fan_cnt = *(chip_regs + MAX6615_REG_FAN_CNT(1)); pwm_target = *(chip_regs + MAX6615_REG_TARGT_PWM(1)); pwm_output = *(chip_regs + MAX6615_REG_INSTANTANEOUS_PWM(1)); printf("%-32s: %.2f C\n", "AIR_OUTLET_TEMP", max6615_temp_calculate(temp)); printf("%-32s: %.2f RPM\n", "FAN2_SPEED", max6615_fan_calculate(fan_cnt)); printf("%-32s: %.2f %%\n", "FAN2_TARGET_PWM", max6615_pwm_calculate(pwm_target)); printf("%-32s: %.2f %%\n", "FAN2_PWM_OUTPUT", max6615_pwm_calculate(pwm_output)); return ret; } static int decode_pem_backup_log(uint8_t num) { int ret = 0; char *chip_regs; for(int chip = LTC4282; chip <= MAX6615; chip++) { chip_regs = malloc(pem[num].chip_reg_cnt[chip]); if(chip_regs == NULL) { ret |= (1 << chip); continue; } size_t len = 0; if(file_read(pem[num].file_path[chip], chip_regs, &len)) { printf("Failed to read %s\n", pem[num].file_path[chip]); free(chip_regs); ret |= (1 << chip); continue; } printf("\nDecode %s: \n", pem[num].file_path[chip]); switch(chip) { case LTC4282: print_ltc4282_crit_sensors(chip_regs); break; case MAX6615: print_max6615_crit_sensors(chip_regs); break; } free(chip_regs); } return ret; } /* * get pem sensors * refer to pal.c */ static void print_pem_sensors(uint8_t num) { for(int i = PEM1_SENSOR_IN_VOLT; i <= PEM1_SENSOR_TEMP3; i++) { float value = 0; int sensor_num = num * PEM1_SENSOR_STATUS_METER_OVERFLOW + i; char sensor_name[64] = {0}; char sensor_units[8] = {0}; pal_get_sensor_name(num + FRU_PEM1, sensor_num, sensor_name); pal_get_sensor_units(num + FRU_PEM1, sensor_num, sensor_units); if (!pal_sensor_read_raw(num + FRU_PEM1, sensor_num, &value)) { printf("\n%-32s: %0.2f %s", sensor_name, value, sensor_units); } else { printf("\n%-32s: %s", sensor_name, "NA"); } } printf("\n"); } static void print_pem_control_reg(pem_control_t *control) { const char *mode[][4] = { {"external", "5%", "10%", "15%"}, {"3.3V", "5V", "12V", "24V"} }; printf("\n%-32s: %d", "On Fault Mask", control->reg_val.values.on_fault_mask); printf("\n%-32s: %d", "On Delay", control->reg_val.values.on_delay); printf("\n%-32s: %d", "On/Enb", control->reg_val.values.on_enb); printf("\n%-32s: %d", "Mass Write Enable", control->reg_val.values.mass_write_enable); printf("\n%-32s: %d", "Fet on", control->reg_val.values.fet_on); printf("\n%-32s: %d", "OC Autoretry", control->reg_val.values.oc_autoretry); printf("\n%-32s: %d", "UV Autoretry", control->reg_val.values.uv_autoretry); printf("\n%-32s: %d", "OV Autoretry", control->reg_val.values.ov_autoretry); printf("\n%-32s: %s", "On FB Mode", mode[0][control->reg_val.values.fb_mode]); printf("\n%-32s: %s", "On UV Mode", mode[0][control->reg_val.values.uv_mode]); printf("\n%-32s: %s", "On OV Mode", mode[0][control->reg_val.values.ov_mode]); printf("\n%-32s: %s", "On Vin Mode", mode[1][control->reg_val.values.vin_mode]); printf("\n"); } static void print_pem_alert_reg(pem_alert_t *alert) { printf("\n%-32s: %d", "EEPROM Done Alert", alert->reg_val.values.eeprom_done_alert); printf("\n%-32s: %d", "FET Bad Fault Alert", alert->reg_val.values.fet_bad_fault_alert); printf("\n%-32s: %d", "FET Short Alert", alert->reg_val.values.fet_short_alert); printf("\n%-32s: %d", "On Alert", alert->reg_val.values.on_alert); printf("\n%-32s: %d", "PB Alert", alert->reg_val.values.pb_alert); printf("\n%-32s: %d", "OC Alert", alert->reg_val.values.oc_alert); printf("\n%-32s: %d", "UV Alert", alert->reg_val.values.uv_alert); printf("\n%-32s: %d", "OV Alert", alert->reg_val.values.ov_alert); printf("\n%-32s: %d", "Power Alarm High", alert->reg_val.values.power_alarm_high); printf("\n%-32s: %d", "Power Alarm Low", alert->reg_val.values.power_alarm_low); printf("\n%-32s: %d", "Vsense Alarm High", alert->reg_val.values.vsense_alarm_high); printf("\n%-32s: %d", "Vsense Alarm Low", alert->reg_val.values.vsense_alarm_low); printf("\n%-32s: %d", "VSourve Alarm High", alert->reg_val.values.vsource_alarm_high); printf("\n%-32s: %d", "VSourve Alarm Low", alert->reg_val.values.vsource_alarm_low); printf("\n%-32s: %d", "VGPIO Alarm High", alert->reg_val.values.vgpio_alarm_high); printf("\n%-32s: %d", "VGPIO Alarm Low", alert->reg_val.values.vgpio_alarm_low); printf("\n"); } static void print_pem_fault_reg(pem_fault_t *fault) { printf("\n%-32s: %d", "EEPROM_Done", fault->reg_val.values.eeprom_done); printf("\n%-32s: %d", "FET_Bad_Fault", fault->reg_val.values.fet_bad_fault); printf("\n%-32s: %d", "FET_Short_Fault", fault->reg_val.values.fet_short_fault); printf("\n%-32s: %d", "On_Fault", fault->reg_val.values.on_fault); printf("\n%-32s: %d", "Power_Bad_Fault", fault->reg_val.values.power_bad_fault); printf("\n%-32s: %d", "OC_Fault", fault->reg_val.values.oc_fault); printf("\n%-32s: %d", "UV_Fault", fault->reg_val.values.uv_fault); printf("\n%-32s: %d", "OV_Fault", fault->reg_val.values.ov_fault); printf("\n"); } static void print_pem_adc_alert_reg(pem_adc_alert_t *adc_alert_log) { printf("\n%-32s: %d", "Power_Alarm_High", adc_alert_log->reg_val.values.power_alarm_high); printf("\n%-32s: %d", "Power_Alarm_Low", adc_alert_log->reg_val.values.power_alarm_low); printf("\n%-32s: %d", "Vsense_Alarm_High", adc_alert_log->reg_val.values.vsense_alarm_high); printf("\n%-32s: %d", "Vsense_Alarm_Low", adc_alert_log->reg_val.values.vsense_alarm_low); printf("\n%-32s: %d", "VSourve_Alarm_High", adc_alert_log->reg_val.values.vsource_alarm_high); printf("\n%-32s: %d", "VSourve_Alarm_Low", adc_alert_log->reg_val.values.vsource_alarm_low); printf("\n%-32s: %d", "VGPIO_Alarm_High", adc_alert_log->reg_val.values.vgpio_alarm_high); printf("\n%-32s: %d", "VGPIO_Alarm_Low", adc_alert_log->reg_val.values.vgpio_alarm_low); printf("\n"); } static void print_pem_gpio_config_reg(pem_gpio_alert_t *gpio_config) { const char *gpio1_config[] = { "Power Good", "Power Bad", "Output", "Input" }; printf("\n%-32s: %d", "GPIO3 PD", gpio_config->reg_val.values.gpio3_pd); printf("\n%-32s: %d", "GPIO2 PD", gpio_config->reg_val.values.gpio2_pd); printf("\n%-32s: %s", "GPIO1 Config", gpio1_config[gpio_config->reg_val.values.gpio1_config]); printf("\n%-32s: %d", "GPIO1 Output", gpio_config->reg_val.values.gpio1_output); printf("\n%-32s: %d", "ADC Conv Alert", gpio_config->reg_val.values.adc_conv_alert); printf("\n%-32s: %d", "Stress to GPIO2", gpio_config->reg_val.values.stress_to_gpio2); printf("\n%-32s: %d", "Meter Overflow Alert", gpio_config->reg_val.values.meter_overflow_alert); printf("\n"); } static void print_pem_clock_divider_reg(pem_clock_divider_t *clock_decimator) { printf("\n%-32s: %d", "Coulomb Meter", clock_decimator->reg_val.values.coulomb_meter); printf("\n%-32s: %d", "Tick Out", clock_decimator->reg_val.values.tick_out); printf("\n%-32s: %d", "Int Clock Out", clock_decimator->reg_val.values.int_clk_out); printf("\n%-32s: %d", "Clock Divider", clock_decimator->reg_val.values.clock_divider); printf("\n"); } static void print_pem_ilim_adjust_reg(pem_ilim_adjust_t *ilim_adjust) { printf("\n%-32s: %d", "ILIM Adjust", ilim_adjust->reg_val.values.ilim_adjust); printf("\n%-32s: %d", "Foldback Mode", ilim_adjust->reg_val.values.foldback_mode); printf("\n%-32s: %d", "Vsource/VDD", ilim_adjust->reg_val.values.vource_vdd); printf("\n%-32s: %d", "GPIO Mode", ilim_adjust->reg_val.values.gpio_mode); printf("\n%-32s: %d-bit", "ADC 16-BIT/12-BIT", ilim_adjust->reg_val.values.adc_resolution ? 16 : 12); printf("\n"); } static void print_pem_status_reg(pem_status_t *status) { printf("\n%-32s: %d", "ON_STATUS", status->reg_val.values.on_status); printf("\n%-32s: %d", "FET_BAD_COOLDOWN_STATUS", status->reg_val.values.fet_bad_cooldown_status); printf("\n%-32s: %d", "FET_SHORT_PRESENT", status->reg_val.values.fet_short_present); printf("\n%-32s: %d", "ON_PIN_STATUS", status->reg_val.values.on_pin_status); printf("\n%-32s: %d", "POWER_GOOD_STATUS", status->reg_val.values.power_good_status); printf("\n%-32s: %d", "OC_COOLDOWN_STATUS", status->reg_val.values.oc_cooldown_status); printf("\n%-32s: %d", "UV_STATUS", status->reg_val.values.uv_status); printf("\n%-32s: %d", "OV_STATUS", status->reg_val.values.ov_status); printf("\n%-32s: %d", "GPIO3_STATUS", status->reg_val.values.gpio3_status); printf("\n%-32s: %d", "GPIO2_STATUS", status->reg_val.values.gpio2_status); printf("\n%-32s: %d", "GPIO1_STATUS", status->reg_val.values.gpio1_status); printf("\n%-32s: %d", "ALERT_STATUS", status->reg_val.values.alert_status); printf("\n%-32s: %d", "EEPROM_BUSY", status->reg_val.values.eeprom_busy); printf("\n%-32s: %d", "ADC_IDLE", status->reg_val.values.adc_idle); printf("\n%-32s: %d", "TICKER_OVERFLOW_PRESENT", status->reg_val.values.ticker_overflow_present); printf("\n%-32s: %d", "METER_OVERFLOW_PRESENT", status->reg_val.values.meter_overflow_present); printf("\n"); } static void print_pem_status_regs(pem_status_regs_t *status_regs) { if(!status_regs) return; print_pem_status_reg(&status_regs->status); print_pem_fault_reg(&status_regs->fault); print_pem_adc_alert_reg(&status_regs->adc_alert_log); } static void print_pem_eeprom_regs(pem_eeprom_reg_t *eeprom_reg) { if(!eeprom_reg) return; print_pem_control_reg(&eeprom_reg->control); print_pem_alert_reg(&eeprom_reg->alert); print_pem_fault_reg(&eeprom_reg->fault); print_pem_adc_alert_reg(&eeprom_reg->adc_alert_log); print_pem_gpio_config_reg(&eeprom_reg->gpio_config); print_pem_clock_divider_reg(&eeprom_reg->clock_decimator); print_pem_ilim_adjust_reg(&eeprom_reg->ilim_adjust); } static void print_pem_fru_eeprom(struct wedge_eeprom_st fruid) { printf("\n%-32s: %d", "Version", fruid.fbw_version); printf("\n%-32s: %s", "Product Name", fruid.fbw_product_name); printf("\n%-32s: %s", "Product Part Number", fruid.fbw_product_number); printf("\n%-32s: %s", "System Assembly Part Number", fruid.fbw_assembly_number); printf("\n%-32s: %s", "Facebook PCBA Part Number", fruid.fbw_facebook_pcba_number); printf("\n%-32s: %s", "Facebook PCB Part Number", fruid.fbw_facebook_pcb_number); printf("\n%-32s: %s", "ODM PCBA Part Number", fruid.fbw_odm_pcba_number); printf("\n%-32s: %s", "ODM PCBA Serial Number", fruid.fbw_odm_pcba_serial); printf("\n%-32s: %d", "Product Production State", fruid.fbw_production_state); printf("\n%-32s: %d", "Product Version", fruid.fbw_product_version); printf("\n%-32s: %d", "Product Sub-Version", fruid.fbw_product_subversion); printf("\n%-32s: %s", "Product Serial Number", fruid.fbw_product_serial); printf("\n%-32s: %s", "Product Asset Tag", fruid.fbw_product_asset); printf("\n%-32s: %s", "System Manufacturer", fruid.fbw_system_manufacturer); printf("\n%-32s: %s", "System Manufacturing Date", fruid.fbw_system_manufacturing_date); printf("\n%-32s: %s", "PCB Manufacturer", fruid.fbw_pcb_manufacturer); printf("\n%-32s: %s", "Assembled At", fruid.fbw_assembled); printf("\n%-32s: %02X:%02X:%02X:%02X:%02X:%02X", "Local MAC", fruid.fbw_local_mac[0], fruid.fbw_local_mac[1], fruid.fbw_local_mac[2], fruid.fbw_local_mac[3], fruid.fbw_local_mac[4], fruid.fbw_local_mac[5]); printf("\n%-32s: %02X:%02X:%02X:%02X:%02X:%02X", "Extended MAC Base", fruid.fbw_mac_base[0], fruid.fbw_mac_base[1], fruid.fbw_mac_base[2], fruid.fbw_mac_base[3], fruid.fbw_mac_base[4], fruid.fbw_mac_base[5]); printf("\n%-32s: %d", "Extended MAC Address Size", fruid.fbw_mac_size); printf("\n%-32s: %s", "Location on Fabric", fruid.fbw_location); printf("\n%-32s: 0x%x", "CRC8", fruid.fbw_crc8); printf("\n"); } static int parse_pem_fru_eeprom(uint8_t num, struct wedge_eeprom_st *fruid) { int ret = -1; char eeprom[16]; snprintf(eeprom, sizeof(eeprom), "%s", "24c02"); i2c_add_device(pem[num].bus, pem[num].chip_addr[EEPROM], eeprom); ret = wedge_eeprom_parse(pem[num].file_path[EEPROM], fruid); i2c_delete_device(pem[num].bus, pem[num].chip_addr[EEPROM]); if (ret) { snprintf(eeprom, sizeof(eeprom), "%s", "24c64"); i2c_add_device(pem[num].bus, pem[num].chip_addr[EEPROM], eeprom); ret = wedge_eeprom_parse(pem[num].file_path[EEPROM], fruid); i2c_delete_device(pem[num].bus, pem[num].chip_addr[EEPROM]); if (ret) { printf("Failed print EEPROM info!\n"); return -1; } } return 0; } /* * dump ltc4282 FAULT_LOG/ADC_ALERT_LOG/STATUS regs */ static int pem_status_regs(uint8_t num, int option, pem_status_regs_t *status_regs) { int value = 0; if (option != READ) return -1; pem[num].fd = i2c_cdev_slave_open(pem[num].bus, pem[num].chip_addr[LTC4282], I2C_SLAVE_FORCE_CLAIM); if (pem[num].fd < 0) { ERR_PRINT("Fail to open i2c"); return pem[num].fd; } pem_reg_read(pem[num].fd, FAULT_LOG, &value); status_regs->fault.reg_val.value = value; pem_reg_read(pem[num].fd, ADC_ALERT_LOG, &value); status_regs->adc_alert_log.reg_val.value = value; pem_reg_read(pem[num].fd, STATUS, &value); status_regs->status.reg_val.value = value; close(pem[num].fd); return 0; } static int pem_firmware_regs(int fd, int option, pem_firmware_regs_t *firmware_regs) { int value = 0; if (option != READ) return -1; if (fd < 0) { ERR_PRINT("Fail to open i2c"); return fd; } pem_reg_read(fd, APP_FW_MAJOR, &value); firmware_regs->app_fw_major = value; pem_reg_read(fd, APP_FW_MINOR, &value); firmware_regs->app_fw_minor = value; pem_reg_read(fd, BL_FW_MAJOR, &value); firmware_regs->bl_fw_major = value; pem_reg_read(fd, BL_FW_MINOR, &value); firmware_regs->bl_fw_minor = value; return 0; } int log_pem_critical_regs(uint8_t num) { pem_status_regs_t status_regs; int ret = 0; ret = pem_status_regs(num, READ, &status_regs); if(ret) { OBMC_ERROR(ret, "Failed to read PEM %d FAULT_LOG/ADC_ALERT_LOG/STATUS registers", num + 1); return ret; } OBMC_CRIT("PEM %d [FAULT_LOG addr: 0x%02x, value: 0x%02x] " \ "[ADC_ALERT_LOG addr: 0x%02x, value: 0x%02x] " \ "[STATUS addr: 0x%02x, value: 0x%04x]", num + 1, smbus_ltc4282[FAULT_LOG].reg, status_regs.fault.reg_val.value, smbus_ltc4282[ADC_ALERT_LOG].reg, status_regs.adc_alert_log.reg_val.value, smbus_ltc4282[STATUS].reg, status_regs.status.reg_val.value); return ret; } /* * dump ltc4282 eeprom regs */ static int pem_eeprom_regs(uint8_t num, int option, pem_eeprom_reg_t *eeprom_reg) { if (option != READ && option != WRITE) return -1; pem[num].fd = i2c_cdev_slave_open(pem[num].bus, pem[num].chip_addr[LTC4282], I2C_SLAVE_FORCE_CLAIM); if (pem[num].fd < 0) { ERR_PRINT("Fail to open i2c"); return -1; } for(int reg = EE_CONTROL; reg <= EE_ILIM_ADJUST; reg++) { int value = 0; if (option == READ) { pem_reg_read(pem[num].fd, reg, &value); switch(reg) { case EE_CONTROL: eeprom_reg->control.reg_val.value = value; break; case EE_ALERT: eeprom_reg->alert.reg_val.value = value; break; case EE_FAULT: eeprom_reg->fault.reg_val.value = value; break; case EE_ADC_ALERT_LOG: eeprom_reg->adc_alert_log.reg_val.value = value; break; case EE_FET_BAD_FAULT_TIME: eeprom_reg->fet_bad_fault_time = value; break; case EE_GPIO_CONFIG: eeprom_reg->gpio_config.reg_val.value = value; break; case EE_VGPIO_ALARM_MIN: eeprom_reg->vgpio_alarm_min = value; break; case EE_VGPIO_ALARM_MAX: eeprom_reg->vgpio_alarm_max = value; break; case EE_VSOURCE_ALARM_MIN: eeprom_reg->vsource_alarm_min = value; break; case EE_VSOURCE_ALARM_MAX: eeprom_reg->vsource_alarm_max = value; break; case EE_VSENSE_ALARM_MIN: eeprom_reg->vsense_alarm_min = value; break; case EE_VSENSE_ALARM_MAX: eeprom_reg->vsense_alarm_max = value; break; case EE_POWER_ALARM_MIN: eeprom_reg->power_alarm_min = value; break; case EE_POWER_ALARM_MAX: eeprom_reg->power_alarm_max = value; break; case EE_CLOCK_DECIMATOR: eeprom_reg->clock_decimator.reg_val.value = value; break; case EE_ILIM_ADJUST: eeprom_reg->ilim_adjust.reg_val.value = value; break; default: continue; } } else if (option == WRITE) { switch(reg) { case EE_CONTROL: value = eeprom_reg->control.reg_val.value; break; case EE_ALERT: value = eeprom_reg->alert.reg_val.value; break; case EE_FAULT: value = eeprom_reg->fault.reg_val.value; break; case EE_ADC_ALERT_LOG: value = eeprom_reg->adc_alert_log.reg_val.value; break; case EE_FET_BAD_FAULT_TIME: value = eeprom_reg->fet_bad_fault_time; break; case EE_GPIO_CONFIG: value = eeprom_reg->gpio_config.reg_val.value; break; case EE_VGPIO_ALARM_MIN: value = eeprom_reg->vgpio_alarm_min; break; case EE_VGPIO_ALARM_MAX: value = eeprom_reg->vgpio_alarm_max; break; case EE_VSOURCE_ALARM_MIN: value = eeprom_reg->vsource_alarm_min; break; case EE_VSOURCE_ALARM_MAX: value = eeprom_reg->vsource_alarm_max; break; case EE_VSENSE_ALARM_MIN: value = eeprom_reg->vsense_alarm_min; break; case EE_VSENSE_ALARM_MAX: value = eeprom_reg->vsense_alarm_max; break; case EE_POWER_ALARM_MIN: value = eeprom_reg->power_alarm_min; break; case EE_POWER_ALARM_MAX: value = eeprom_reg->power_alarm_max; break; case EE_CLOCK_DECIMATOR: value = eeprom_reg->clock_decimator.reg_val.value; break; case EE_ILIM_ADJUST: value = eeprom_reg->ilim_adjust.reg_val.value; break; default: continue; } pem_reg_write(pem[num].fd, reg, value); } } close(pem[num].fd); return 0; } /* * Populate and print fruid_info by parsing the fru's binary dump and * populate and print ltc4282 eeprom regs */ int get_eeprom_info(uint8_t num, const char *option) { struct wedge_eeprom_st fruid; pem_eeprom_reg_t eeprom_reg; if ((!strncmp(option, "--print", strlen("--print")))) { printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "FRU Information", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); if(!parse_pem_fru_eeprom(num, &fruid)) { print_pem_fru_eeprom(fruid); } printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "Hot Swap EEPROM Information", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); pem_eeprom_regs(num, READ, &eeprom_reg); print_pem_eeprom_regs(&eeprom_reg); } else if ((!strncmp(option, "--clear", strlen("--clear")))) { printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "Hot Swap EEPROM Default Information", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); print_pem_eeprom_regs(&pem_default_config); pem_eeprom_regs(num, WRITE, &pem_default_config); } else { printf("Invalid option!\n"); return -1; } return 0; } /* * Show pem current status & some basic info */ int get_pem_info(uint8_t num) { struct wedge_eeprom_st fruid; pem_status_regs_t status_regs; pem_firmware_regs_t firmware_regs = {0}; int ret; uint8_t pem_model[I2C_SMBUS_BLOCK_MAX + 1] = {0}; printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "PEM Information", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); if(!parse_pem_fru_eeprom(num, &fruid)) { printf("\n%-32s: %d", "Version", fruid.fbw_version); printf("\n%-32s: %s", "Product Name", fruid.fbw_product_name); printf("\n%-32s: %s", "Product Part Number", fruid.fbw_product_number); printf("\n%-32s: %d", "Product Version", fruid.fbw_product_version); printf("\n%-32s: %d", "Product Sub-Version", fruid.fbw_product_subversion); printf("\n%-32s: %s", "Product Serial Number", fruid.fbw_product_serial); printf("\n%-32s: %s", "System Manufacturer", fruid.fbw_system_manufacturer); printf("\n%-32s: %s", "System Manufacturing Date", fruid.fbw_system_manufacturing_date); printf("\n"); } ret = get_mfr_model(num, pem_model); if (ret == 0) { if (!strncmp((const char *)pem_model, DELTA_MODEL, strlen(DELTA_MODEL))) { if (pem[num].fd < 0) { pem[num].fd = i2c_cdev_slave_open(pem[num].bus, pem[num].chip_addr[LTC4282], I2C_SLAVE_FORCE_CLAIM); } ret = pem_firmware_regs(pem[num].fd, READ, &firmware_regs); if (ret == 0) { printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "PEM Firmware Info", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); printf("\n%-32s: %d.%d", "Application Version", firmware_regs.app_fw_major, firmware_regs.app_fw_minor); printf("\n%-32s: %d.%d", "Bootloader Version", firmware_regs.bl_fw_major, firmware_regs.bl_fw_minor); printf("\n"); } } } printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "PEM Hot Swap status", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); print_pem_sensors(num); pem_status_regs(num, READ, &status_regs); print_pem_status_regs(&status_regs); return 0; } /* * Show pem's info & status fully */ int get_blackbox_info(uint8_t num, const char *option) { struct wedge_eeprom_st fruid; pem_eeprom_reg_t eeprom_reg; pem_status_regs_t status_regs; pem[num].fd = i2c_cdev_slave_open(pem[num].bus, pem[num].chip_addr[LTC4282], I2C_SLAVE_FORCE_CLAIM); if (pem[num].fd < 0) { ERR_PRINT("Fail to open i2c"); return -1; } if ((!strncmp(option, "--print", strlen("--print")))) { printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "FRU Information", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); if(!parse_pem_fru_eeprom(num, &fruid)) { print_pem_fru_eeprom(fruid); } printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "Hot Swap EEPROM Information", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); pem_eeprom_regs(num, READ, &eeprom_reg); print_pem_eeprom_regs(&eeprom_reg); printf("\n%-32s: PEM%d (Bus:%d Addr:0x%x)", "PEM Hot Swap status", num + 1, pem[num].bus, pem[num].chip_addr[LTC4282]); printf("\n%-32s: %s", "---------------", "-----------------------"); print_pem_sensors(num); pem_status_regs(num, READ, &status_regs); print_pem_status_regs(&status_regs); printf("\n"); } else if ((!strncmp(option, "--clear", strlen("--clear")))) { printf("Not support!\n"); } else { printf("Invalid option!\n"); close(pem[num].fd); return -1; } close(pem[num].fd); return 0; } int get_archive_log(uint8_t num, const char *option) { if ((!strncmp(option, "--print", strlen("--print")))) { return decode_pem_backup_log(num); } else if ((!strncmp(option, "--clear", strlen("--clear")))) { printf("Not support!\n"); return 0; } else { printf("Invalid option!\n"); close(pem[num].fd); return -1; } } static int delta_check_fw_version(uint8_t num) { pem_firmware_regs_t firmware_regs = {0}; int ret; uint16_t app_fw_ver, bl_fw_ver; uint16_t eep_app_fw_ver, eep_bl_fw_ver; ret = pem_firmware_regs(pem[num].fd, READ, &firmware_regs); if (ret != 0) { ERR_PRINT("delta_check_fw_version()"); return -1; } app_fw_ver = (delta_hdr.app_fw_major << 8) | delta_hdr.app_fw_minor; bl_fw_ver = (delta_hdr.bl_fw_major << 8) | delta_hdr.bl_fw_minor; eep_app_fw_ver = (firmware_regs.app_fw_major << 8) | firmware_regs.app_fw_minor; eep_bl_fw_ver = (firmware_regs.bl_fw_major << 8) | firmware_regs.bl_fw_minor; if (app_fw_ver == eep_app_fw_ver && bl_fw_ver == eep_bl_fw_ver) { /* FW is identical */ return FW_IDENTICAL; } return 0; } static int check_file_len(const char *file_path) { int size; struct stat st; if (stat(file_path, &st) != 0) { ERR_PRINT("check_file_len()"); return -1; } size = st.st_size; return size; } static int delta_img_hdr_parse(const char *file_path) { int ret; int i; int fd_file = -1; int index = 0; uint8_t hdr_buf[DELTA_HDR_LENGTH]; fd_file = open(file_path, O_RDONLY, 0666); if (fd_file < 0) { ERR_PRINT("delta_img_hdr_parse(): open()"); return -1; } ret = read(fd_file, hdr_buf, DELTA_HDR_LENGTH); if (ret != DELTA_HDR_LENGTH) { ERR_PRINT("delta_img_hdr_parse(): read()"); if(close(fd_file)) { ERR_PRINT("delta_img_hdr_parse(): close()"); } return -1; } if(close(fd_file)) { ERR_PRINT("delta_img_hdr_parse(): close()"); return -1; } delta_hdr.crc[0] = hdr_buf[index++]; delta_hdr.crc[1] = hdr_buf[index++]; delta_hdr.page_start = hdr_buf[index++]; delta_hdr.page_start = ( delta_hdr.page_start << 8 ) | hdr_buf[index++]; delta_hdr.page_end = hdr_buf[index++]; delta_hdr.page_end = ( delta_hdr.page_end << 8 ) | hdr_buf[index++]; delta_hdr.byte_per_blk = hdr_buf[index++]; delta_hdr.byte_per_blk =( delta_hdr.byte_per_blk << 8 ) | hdr_buf[index++]; delta_hdr.blk_per_page = hdr_buf[index++]; delta_hdr.blk_per_page = ( delta_hdr.blk_per_page << 8 ) | hdr_buf[index++]; delta_hdr.uc = hdr_buf[index++]; delta_hdr.app_fw_major = hdr_buf[index++]; delta_hdr.app_fw_minor = hdr_buf[index++]; delta_hdr.bl_fw_major = hdr_buf[index++]; delta_hdr.bl_fw_minor = hdr_buf[index++]; delta_hdr.fw_id_len = hdr_buf[index++]; for (i = 0; i < delta_hdr.fw_id_len; i++) { delta_hdr.fw_id[i] = hdr_buf[index++]; } delta_hdr.compatibility = hdr_buf[index]; if (!strncmp((const char *)delta_hdr.fw_id, DELTA_MODEL, strlen(DELTA_MODEL))) { printf("Vendor: Delta\n"); printf("Model: %s\n", delta_hdr.fw_id); printf("HW Compatibility: %d\n", delta_hdr.compatibility); if (delta_hdr.uc == 0x10) { printf("MCU: primary\n"); } else if (delta_hdr.uc == 0x20) { printf("MCU: secondary\n"); } else { printf("MCU: unknown number 0x%x\n", delta_hdr.uc); return -1; } printf("Ver: %d.%d\n", delta_hdr.app_fw_major, delta_hdr.app_fw_minor); return DELTA_PEM; } else { printf("Get Image Header Fail!\n"); return -1; } } static int delta_unlock_upgrade(uint8_t num) { uint8_t block[I2C_SMBUS_BLOCK_MAX] = {delta_hdr.uc, delta_hdr.fw_id[10], delta_hdr.fw_id[9], delta_hdr.fw_id[8], delta_hdr.fw_id[7], delta_hdr.fw_id[6], delta_hdr.fw_id[5], delta_hdr.fw_id[4], delta_hdr.fw_id[3], delta_hdr.fw_id[2], delta_hdr.fw_id[1], delta_hdr.fw_id[0], delta_hdr.compatibility}; printf("-- Unlock Upgrade --\n"); int ret = i2c_smbus_write_block_data(pem[num].fd, UNLOCK_UPGRADE, 13, block); if (ret < 0) { printf("Unlock upgrade failure\n"); return -1; } else return 0; } static int delta_boot_flag(uint8_t num, uint16_t mode) { int ret; uint16_t word = (mode << 8) | delta_hdr.uc; printf("-- %s --\n", mode == BOOT_MODE ? "Bootloader Mode" : "Reset PEM"); ret = i2c_smbus_write_word_data(pem[num].fd, BOOT_FLAG, word); if (ret < 0) { printf("Failed to %s\n", mode == BOOT_MODE ? "set to bootloader mode" : "reset PEM"); return -1; } else return 0; } static int delta_fw_transmit(uint8_t num, const char *path) { int ret; FILE* fp; int fw_len = 0; int block_total = 0; int byte_index = 0; int fw_block = (delta_hdr.uc == 0x10) ? DELTA_PRI_NUM_OF_BLOCK * DELTA_PRI_NUM_OF_PAGE : DELTA_SEC_NUM_OF_BLOCK * DELTA_SEC_NUM_OF_PAGE; uint8_t page_num_lo = (delta_hdr.uc == 0x10) ? DELTA_PRI_PAGE_START : DELTA_SEC_PAGE_START; uint8_t block_size = (delta_hdr.uc == 0x10) ? DELTA_PRI_NUM_OF_BLOCK : DELTA_SEC_NUM_OF_BLOCK; uint8_t page_num_max = (delta_hdr.uc == 0x10) ? DELTA_PRI_PAGE_END : DELTA_SEC_PAGE_END; uint8_t block[I2C_SMBUS_BLOCK_MAX] = {0}; uint8_t fw_buf[16]; fw_len = check_file_len(path) - 32; uint8_t fw_data[fw_len]; fp = fopen(path, "rb"); if (fp == NULL) { ERR_PRINT("delta_fw_transmit(): fopen()"); return -1; } ret = fseek(fp, 32, SEEK_SET); if (ret < 0) { ERR_PRINT("delta_fw_transmit(): fseek()"); goto err_exit; } ret = fread(fw_data, sizeof(uint8_t), fw_len, fp); if (ret != fw_len) { ret = feof(fp); if (ret < 0) { ERR_PRINT("delta_fw_transmit(): fread()"); goto err_exit; } } if (delta_hdr.uc == 0x10) { printf("-- Transmit Primary Firmware --\n"); } else if (delta_hdr.uc == 0x20){ printf("-- Transmit Secondary Firmware --\n"); } /* Send date to PEM, use 16bytes mode */ while (block_total <= fw_block) { block[0] = delta_hdr.uc; /* block[1] - Block Num LO block[2] - Block Num HI */ if (block[1] < block_size) { memcpy(&fw_buf[0], &fw_data[byte_index], 16); memcpy(&block[3], &fw_buf, 16); ret = i2c_smbus_write_block_data(pem[num].fd, DATA_TO_RAM, 19, block); if (ret < 0) { printf("Send data to RAM failure\n"); goto err_exit; } /* * The delay between two blocks of data. * It should be no less than 5ms, 10ms is recommended. */ if (delta_hdr.uc == 0x10) { msleep(10); } else if (delta_hdr.uc == 0x20) { msleep(10); } block[1]++; block[2] = 0; block_total++; byte_index = byte_index + 16; printf("-- (%d/%d) (%d%%/100%%) --\r", block_total, fw_block, (100 * block_total) / fw_block); } else { block[1] = page_num_lo; block[2] = 0; ret = i2c_smbus_write_block_data(pem[num].fd, DATA_TO_FLASH, 3, block); if (ret < 0) { printf("Send data to flash failure\n"); goto err_exit; } /* * The delay between write data to flash command F3h and write CRC value command F4h. * It should be no less than 50ms, 90ms is recommended. */ msleep(90); if (page_num_lo == page_num_max) { printf("\n"); goto exit; } else { page_num_lo++; block[1] = 0; } } } exit: ret = 0; err_exit: ret = fclose(fp); if (ret < 0) { ERR_PRINT("delta_fw_transmit(): fclose()"); } return ret; } static int delta_crc_transmit(uint8_t num) { uint8_t block[I2C_SMBUS_BLOCK_MAX] = {delta_hdr.uc, delta_hdr.crc[0], delta_hdr.crc[1]}; printf("-- Transmit CRC --\n"); int ret = i2c_smbus_write_block_data(pem[num].fd, CRC_CHECK, 3, block); if (ret < 0) { printf("CRC check failure\n"); return -1; } return 0; } static int update_delta_pem(uint8_t num, const char *file_path, _Bool force) { int ret = 0; if (delta_img_hdr_parse(file_path) == DELTA_PEM) { if (!force) { printf("-- Check Version --\n"); ret = delta_check_fw_version(num); if (ret != 0) { return ret; } } ret = delta_unlock_upgrade(num); if (ret < 0) goto err_exit; /* * The delay between unlock command F0h and set boot flag command F1h. * It should be no less than 10ms, 20ms is recommended. */ msleep(20); ret = delta_boot_flag(num, BOOT_MODE); if (ret < 0) goto err_exit; /* * The delay between unlock command F1h and send data command F2h. * It should be no less than 1500ms, 2500ms is recommended. */ msleep(2500); ret = delta_fw_transmit(num, file_path); if (ret < 0) goto err_exit; ret = delta_crc_transmit(num); if (ret < 0) goto err_exit; /* * The delay between write CRC value command F4h and reset boot flag command F1h. * It should be no less than 800, 1500ms is recommended. */ msleep(1500); ret = delta_boot_flag(num, NORMAL_MODE); /* * The delay from reset boot flag command to power supply back to normal working status, * which allowed to process the next controller. * It should be no less than 1000, 2000ms is recommended. */ if (delta_hdr.uc == 0x10) { msleep(2000); } else if (delta_hdr.uc == 0x20) { msleep(2000); } printf("-- Upgrade Done --\n"); return 0; } err_exit: delta_boot_flag(num, NORMAL_MODE); return -1; } int get_mfr_model(uint8_t num, uint8_t *block) { int rc = -1; struct wedge_eeprom_st fruid; rc = i2c_smbus_read_block_data(pem[num].fd, PMBUS_MFR_MODEL, block); if (rc < 0) { rc = parse_pem_fru_eeprom(num, &fruid); if (rc < 0) { ERR_PRINT("get_mfr_model()"); return -1; } snprintf((char *)block, 33 , "%s", fruid.fbw_odm_pcba_number); } return 0; } int do_update_pem(uint8_t num, const char *file_path, const char *vendor, _Bool force) { int ret = -1; uint8_t block[I2C_SMBUS_BLOCK_MAX + 1] = {0}; signal(SIGHUP, exithandler); signal(SIGINT, exithandler); signal(SIGTERM, exithandler); signal(SIGQUIT, exithandler); sensord_operation(num, STOP); i2c_delete_device(pem[num].bus, pem[num].chip_addr[LTC4282]); i2c_delete_device(pem[num].bus, pem[num].chip_addr[MAX6615]); pem[num].fd = i2c_cdev_slave_open(pem[num].bus, pem[num].chip_addr[LTC4282], I2C_SLAVE_FORCE_CLAIM); g_fd = pem[num].fd; if (pem[num].fd < 0) { ERR_PRINT("Fail to open i2c"); return -1; } if (vendor == NULL) { ret = get_mfr_model(num, block); if (ret < 0) { printf("Cannot Get PEM Model\n"); return -1; } } if ((vendor == NULL && !strncmp((const char *)block, DELTA_MODEL, strlen(DELTA_MODEL))) || !strncasecmp(vendor, "delta", strlen("delta"))) { ret = update_delta_pem(num, file_path, force); } else { printf("Unsupported vendor: %s\n", vendor); return -1; } if (ret == 0 || ret == FW_IDENTICAL) { sensord_operation(num, START); i2c_add_device(pem[num].bus, pem[num].chip_addr[LTC4282], PEM_LTC4282_DRIVER); i2c_add_device(pem[num].bus, pem[num].chip_addr[MAX6615], PEM_MAX6615_DRIVER); } close(pem[num].fd); return ret; }