meta-facebook/meta-minilaketb/recipes-minilaketb/plat-libs/files/pal/pal.c (3,152 lines of code) (raw):

/* * * Copyright 2015-present Facebook. All Rights Reserved. * * This file contains code to support IPMI2.0 Specificaton available @ * http://www.intel.com/content/www/us/en/servers/ipmi/ipmi-specifications.html * * 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 <ctype.h> #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <fcntl.h> #include <errno.h> #include <syslog.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/mman.h> #include <sys/time.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include "pal.h" #include "pal_sensors.h" #include <facebook/bic.h> #include <openbmc/kv.h> #include <openbmc/obmc-i2c.h> #define BIT(value, index) ((value >> index) & 1) #define MINILAKETB_PLATFORM_NAME "MINILAKETB" #define LAST_KEY "last_key" #define MINILAKETB_MAX_NUM_SLOTS 1 #define GPIO_VAL "/sys/class/gpio/gpio%d/value" #define GPIO_DIR "/sys/class/gpio/gpio%d/direction" #define PAGE_SIZE 0x1000 #define AST_SCU_BASE 0x1e6e2000 #define PIN_CTRL1_OFFSET 0x80 #define PIN_CTRL2_OFFSET 0x84 #define WDT_OFFSET 0x3C #define UART1_TXD (1 << 22) #define UART2_TXD (1 << 30) #define UART3_TXD (1 << 22) #define UART4_TXD (1 << 30) #define AST_VIC_BASE 0x1e6c0000 #define HW_HB_STATUS_OFFSET 0x60 #define HB_LED_OUTPUT_OFFSET 0x64 #define SW_BLINK (1 << 4) #define AST_GPIO_BASE 0x1e780000 #define GPIO_AB_AA_Z_Y 0x1e0 #define DELAY_GRACEFUL_SHUTDOWN 1 #define DELAY_POWER_OFF 6 #define DELAY_POWER_CYCLE 10 #if 0 // 20 seconds is a temporarily workaround for voltage leakage #define DELAY_12V_CYCLE 5 #else #define DELAY_12V_CYCLE 20 #endif #define LARGEST_DEVICE_NAME 120 #define PWM_DIR "/sys/devices/platform/ast_pwm_tacho.0" #define PWM_UNIT_MAX 96 #define OFFSET_DEV_GUID 0x1800 #define FRU_EEPROM "/sys/devices/platform/ast-i2c.8/i2c-8/8-0051/eeprom" #define MAX_READ_RETRY 10 #define MAX_CHECK_RETRY 2 #define MAX_BIC_CHECK_RETRY 15 #define PLATFORM_FILE "/tmp/system.bin" #define SLOT_RECORD_FILE "/tmp/slot%d.rc" #define HOTSERVICE_SCRPIT "/usr/local/bin/hotservice-reinit.sh" #define HOTSERVICE_FILE "/tmp/slot%d_reinit" #define HOTSERVICE_PID "/tmp/hotservice_reinit.pid" #define FRUID_SIZE 256 #define EEPROM_DC "/sys/class/i2c-adapter/i2c-%d/%d-0051/eeprom" #define BIN_SLOT "/tmp/fruid_slot%d.bin" #define GMAC0_DIR "/sys/devices/platform/ftgmac100.0/net/eth0" #define SPB_REV_FILE "/tmp/spb_rev" #define SPB_REV_PVT 3 #define IMC_VER_SIZE 8 const char pal_fru_list[] = "all, slot1, spb"; const char pal_server_list[] = "slot1"; size_t pal_pwm_cnt = 2; size_t pal_tach_cnt = 2; const char pal_pwm_list[] = "0, 1"; const char pal_tach_list[] = "0, 1"; uint8_t g_dev_guid[GUID_SIZE] = {0}; static unsigned char m_slot_ac_start[MAX_NODES] = {0}; static bool m_sled_ac_start = false; static void *m_gpio_hand_sw = NULL; static void *m_hbled_output = NULL; typedef struct { uint16_t flag; float ucr; float unc; float unr; float lcr; float lnc; float lnr; float pos_hyst; float neg_hyst; int curr_st; char name[32]; } _sensor_thresh_t; typedef struct { uint16_t flag; float unr; float ucr; float lcr; uint8_t retry_cnt; uint8_t val_valid; float last_val; } sensor_check_t; static sensor_check_t m_snr_chk[MAX_NUM_FRUS][MAX_SENSOR_NUM + 1] = {0}; typedef struct { char name[32]; } sensor_desc_t; static sensor_desc_t m_snr_desc[MAX_NUM_FRUS][MAX_SENSOR_NUM + 1] = {0}; char * key_list[] = { "server_pcie_port_config", "pwr_server1_last_state", "sysfw_ver_slot1", "identify_slot1", "timestamp_sled", "slot1_por_cfg", "slot1_sensor_health", "spb_sensor_health", "slot1_sel_error", "slot1_boot_order", "slot1_cpu_ppin", "fru1_restart_cause", /* Add more Keys here */ LAST_KEY /* This is the last key of the list */ }; char * def_val_list[] = { "on", /* pwr_server1_last_state */ "0", /* sysfw_ver_slot1 */ "off", /* identify_slot1 */ "0", /* timestamp_sled */ "lps", /* slot1_por_cfg */ "1", /* slot1_sensor_health */ "1", /* spb_sensor_health */ "1", /* slot1_sel_error */ "0000000", /* slot1_boot_order */ "0", /* slot1_cpu_ppin */ "3", /* fru1_restart_cause */ /* Add more def values for the correspoding keys*/ LAST_KEY /* Same as last entry of the key_list */ }; struct power_coeff { float val; float coeff; }; static const struct power_coeff curr_cali_table[] = { { 5.56, 0.90556 }, { 10.96, 0.91518 }, { 16.37, 0.91826 }, { 21.78, 0.91959 }, { 27.18, 0.92020 }, { 32.61, 0.92107 }, { 38.01, 0.92123 }, { 43.41, 0.92186 }, { 48.83, 0.92190 }, { 54.22, 0.92221 }, { 59.63, 0.92259 }, { 0.0, 0.0 } }; static const struct power_coeff pwr_cali_table[] = { { 67.95, 0.90602 }, { 132.98, 0.91589 }, { 198.05, 0.91815 }, { 262.54, 0.91984 }, { 326.86, 0.92008 }, { 390.79, 0.92119 }, { 454.36, 0.92127 }, { 517.45, 0.92182 }, { 580.07, 0.92221 }, { 642.23, 0.92253 }, { 704.14, 0.92299 }, { 0.0, 0.0 } }; //check power policy and power state to power on/off server after AC power restore static void pal_power_policy_control(uint8_t slot_id, char *last_ps) { uint8_t chassis_status[5] = {0}; uint8_t chassis_status_length; uint8_t power_policy = POWER_CFG_UKNOWN; char pwr_state[MAX_VALUE_LEN] = {0}; //get power restore policy //defined by IPMI Spec/Section 28.2. pal_get_chassis_status(slot_id, NULL, chassis_status, &chassis_status_length); //byte[1], bit[6:5]: power restore policy power_policy = (*chassis_status >> 5); //Check power policy and last power state if(power_policy == POWER_CFG_LPS) { if (!last_ps) { pal_get_last_pwr_state(slot_id, pwr_state); last_ps = pwr_state; } if (!(strcmp(last_ps, "on"))) { sleep(3); pal_set_server_power(slot_id, SERVER_POWER_ON); } } else if(power_policy == POWER_CFG_ON) { sleep(3); pal_set_server_power(slot_id, SERVER_POWER_ON); } } /* curr/power calibration */ static void power_value_adjust(const struct power_coeff *table, float *value) { float x0, x1, y0, y1, x; int i; x = *value; x0 = table[0].val; y0 = table[0].coeff; if (x0 >= *value) { *value = x * y0; return; } for (i = 1; table[i].val > 0.0; i++) { if (*value < table[i].val) break; x0 = table[i].val; y0 = table[i].coeff; } if (table[i].val <= 0.0) { *value = x * y0; return; } // if value is bwtween x0 and x1, use linear interpolation method. x1 = table[i].val; y1 = table[i].coeff; *value = (y0 + (((y1 - y0)/(x1 - x0)) * (x - x0))) * x; return; } typedef struct _inlet_corr_t { uint8_t duty; int8_t delta_t; } inlet_corr_t; static inlet_corr_t g_ict[] = { // Inlet Sensor: // duty cycle vs delta_t { 10, 2 }, { 16, 1 }, { 38, 0 }, }; static uint8_t g_ict_count = sizeof(g_ict)/sizeof(inlet_corr_t); static void apply_inlet_correction(float *value) { static int8_t dt = 0; int i; uint8_t pwm[2] = {0}; // Get PWM value if (pal_get_pwm_value(0, &pwm[0]) || pal_get_pwm_value(1, &pwm[1])) { // If error reading PWM value, use the previous deltaT *value -= dt; return; } pwm[0] = (pwm[0] + pwm[1]) /2; // Scan through the correction table to get correction value for given PWM dt=g_ict[0].delta_t; for (i=0; i< g_ict_count; i++) { if (pwm[0] >= g_ict[i].duty) dt = g_ict[i].delta_t; else break; } // Apply correction for the sensor *(float*)value -= dt; } // Helper Functions static int read_device(const char *device, int *value) { FILE *fp; int rc; fp = fopen(device, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", device); #endif return err; } rc = fscanf(fp, "%d", value); fclose(fp); if (rc != 1) { #ifdef DEBUG syslog(LOG_INFO, "failed to read device %s", device); #endif return ENOENT; } else { return 0; } } static int write_device(const char *device, const char *value) { FILE *fp; int rc; fp = fopen(device, "w"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device for write %s", device); #endif return err; } rc = fputs(value, fp); fclose(fp); if (rc < 0) { #ifdef DEBUG syslog(LOG_INFO, "failed to write device %s", device); #endif return ENOENT; } else { return 0; } } static int pal_key_check(char *key) { int i; i = 0; while(strcmp(key_list[i], LAST_KEY)) { // If Key is valid, return success if (!strcmp(key, key_list[i])) return 0; i++; } #ifdef DEBUG syslog(LOG_WARNING, "pal_key_check: invalid key - %s", key); #endif return -1; } int pal_get_key_value(char *key, char *value) { // Check is key is defined and valid if (pal_key_check(key)) return -1; return kv_get(key, value, NULL, KV_FPERSIST); } int pal_set_key_value(char *key, char *value) { // Check is key is defined and valid if (pal_key_check(key)) return -1; return kv_set(key, value, 0, KV_FPERSIST); } // Common IPMB Wrapper function static int pal_get_ipmb_bus_id(uint8_t slot_id) { int bus_id; switch(slot_id) { case FRU_SLOT1: bus_id = IPMB_BUS_SLOT1; break; default: bus_id = -1; break; } return bus_id; } /* * pal_copy_eeprom_to_bin - copy the eeprom to binary file im /tmp directory * * @eeprom_file : path for the eeprom of the device * @bin_file : path for the binary file * * returns 0 on successful copy * returns non-zero on file operation errors */ int pal_copy_eeprom_to_bin(const char * eeprom_file, const char * bin_file) { int eeprom; int bin; uint64_t tmp[FRUID_SIZE]; ssize_t bytes_rd, bytes_wr; errno = 0; if (access(eeprom_file, F_OK) != -1) { eeprom = open(eeprom_file, O_RDONLY); if (eeprom == -1) { syslog(LOG_ERR, "pal_copy_eeprom_to_bin: unable to open the %s file: %s", eeprom_file, strerror(errno)); return errno; } bin = open(bin_file, O_WRONLY | O_CREAT, 0644); if (bin == -1) { syslog(LOG_ERR, "pal_copy_eeprom_to_bin: unable to create %s file: %s", bin_file, strerror(errno)); return errno; } bytes_rd = read(eeprom, tmp, FRUID_SIZE); if (bytes_rd != FRUID_SIZE) { syslog(LOG_ERR, "pal_copy_eeprom_to_bin: write to %s file failed: %s", eeprom_file, strerror(errno)); return errno; } bytes_wr = write(bin, tmp, bytes_rd); if (bytes_wr != bytes_rd) { syslog(LOG_ERR, "pal_copy_eeprom_to_bin: write to %s file failed: %s", bin_file, strerror(errno)); return errno; } close(bin); close(eeprom); } return 0; } // Update the Reset button input to the server at given slot int pal_set_rst_btn(uint8_t slot, uint8_t status) { char path[64] = {0}; char *val; if (slot != 1) { return -1; } if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, GPIO_RST_SLOT1_SYS_RESET_N); if (write_device(path, val)) { return -1; } return 0; } int pal_fruid_init(uint8_t slot_id) { int ret=0; char path[128] = {0}; char fpath[64] = {0}; switch(slot_id) { case FRU_SLOT1: switch(minilaketb_get_slot_type(slot_id)) { case SLOT_TYPE_SERVER: // Do not access EEPROM break; case SLOT_TYPE_CF: case SLOT_TYPE_GP: sprintf(path, EEPROM_DC, pal_get_ipmb_bus_id(slot_id), pal_get_ipmb_bus_id(slot_id)); sprintf(fpath, BIN_SLOT, slot_id); ret = pal_copy_eeprom_to_bin(path, fpath); break; case SLOT_TYPE_NULL: // Do not access EEPROM break; } break; default: break; } return ret; } static int power_on_server_physically(uint8_t slot_id){ char vpath[64] = {0}; uint8_t ret = -1; uint8_t retry = MAX_READ_RETRY; bic_gpio_t gpio; syslog(LOG_WARNING, "%s is on going for slot%d\n",__func__,slot_id); sprintf(vpath, GPIO_VAL, GPIO_PWR_SLOT1_BTN_N); if (write_device(vpath, "1")) { return -1; } if (write_device(vpath, "0")) { return -1; } sleep(1); if (write_device(vpath, "1")) { return -1; } // Wait for server power good ready sleep(2); while (retry) { ret = bic_get_gpio(slot_id, &gpio); if (!ret) { #ifdef DEBUG syslog(LOG_WARNING, "%s: Get response successfully for slot%d\n",__func__,slot_id); #endif break; } msleep(50); retry--; } if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "%s: Bridge IC is no response for slot%d\n",__func__,slot_id); #endif return -1; } // Check power status if (!gpio.pwrgood_cpu) { syslog(LOG_WARNING, "%s: Power on is failed for slot%d\n",__func__,slot_id); return -1; } return 0; } #ifdef SUPPORT_WRITE_GMAC0 // Disable unused function till its need arises. #define MAX_POWER_PREP_RETRY_CNT 3 static int write_gmac0_value(const char *device_name, const int value) { char full_name[LARGEST_DEVICE_NAME]; char output_value[LARGEST_DEVICE_NAME]; int err; int retry_cnt=0; snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", GMAC0_DIR, device_name); snprintf(output_value, LARGEST_DEVICE_NAME, "%d", value); do { err = write_device(full_name, output_value); if (err == ENOENT) { syslog(LOG_INFO, "Retry powerup_prep for host%d [%d], err=%d", value, (retry_cnt+1), err); break; } else if (err != 0) { syslog(LOG_INFO, "Retry powerup_prep for host%d [%d], err=%d", value, (retry_cnt+1), err); sleep(1); } } while ((err != 0) && (retry_cnt++ < MAX_POWER_PREP_RETRY_CNT)); return err; } #endif // Power On the server in a given slot static int server_power_on(uint8_t slot_id) { int loop = 0; int max_retry = 5; int val = 0; if (slot_id != 1) { return -1; } // Power on server for (loop = 0; loop < max_retry; loop++){ val = power_on_server_physically(slot_id); if (val == 0) { break; } syslog(LOG_WARNING, "%s(): Power on server failed for %d time(s).\n", __func__, loop); sleep(1); // Max retry case if (loop == (max_retry-1)) return -1; } return 0; } // Power Off the server in given slot static int server_power_off(uint8_t slot_id, bool gs_flag) { char vpath[64] = {0}; if (slot_id != 1) { return -1; } sprintf(vpath, GPIO_VAL, GPIO_PWR_SLOT1_BTN_N); if (write_device(vpath, "1")) { return -1; } sleep(1); if (write_device(vpath, "0")) { return -1; } if (gs_flag) { sleep(DELAY_GRACEFUL_SHUTDOWN); } else { sleep(DELAY_POWER_OFF); } if (write_device(vpath, "1")) { return -1; } return 0; } static uint8_t _get_spb_rev(void) { int rev; if (read_device(SPB_REV_FILE, &rev)) { printf("Get spb revision failed\n"); return -1; } return rev; } int pal_baseboard_clock_control(uint8_t slot_id, char *ctrl) { char v1path[64] = {0}; char v2path[64] = {0}; char v3path[64] = {0}; uint8_t rev; rev = _get_spb_rev(); switch(slot_id) { case FRU_SLOT1: if (rev < SPB_REV_PVT) { sprintf(v1path, GPIO_VAL, GPIO_PE_BUFF_OE_0_R_N); sprintf(v2path, GPIO_VAL, GPIO_PE_BUFF_OE_1_R_N); } sprintf(v3path, GPIO_VAL, GPIO_CLK_BUFF1_PWR_EN_N); break; default: return -1; } if (rev < SPB_REV_PVT) { if (write_device(v1path, ctrl) || write_device(v2path, ctrl)) return -1; } return 0; } int pal_is_server_12v_on(uint8_t slot_id, uint8_t *status) { int val; char path[64] = {0}; sprintf(path, GPIO_VAL, GPIO_P12V_STBY_SLOT1_EN); if (read_device(path, &val)) { return -1; } if (val == 0x1) {//XG1 is low to enable MB_P12V_STBY *status = 0; } else { *status = 1; } return 0; } bool pal_is_hsvc_ongoing(uint8_t slot_id) { char key[MAX_KEY_LEN] = {0}; char value[MAX_VALUE_LEN] = {0}; sprintf(key, "fru%u_hsvc", slot_id); if (kv_get(key, value, NULL, 0)) { return false; } if (atoi(value)) return true; return false; } int pal_set_hsvc_ongoing(uint8_t slot_id, uint8_t status, uint8_t ident) { char key[MAX_KEY_LEN]; sprintf(key, "fru%u_hsvc", slot_id); if (kv_set(key, (status) ? "1" : "0", 0, 0)) { return -1; } if (ident) { sprintf(key, "identify_slot%u", slot_id); if (pal_set_key_value(key, (status) ? "on" : "off")) { syslog(LOG_ERR, "pal_set_key_value: set %s failed", key); return -1; } } return 0; } // Turn off 12V for the server in given slot static int server_12v_off(uint8_t slot_id) { char vpath[64] = {0}; if (slot_id != 1) { return -1; } sprintf(vpath, GPIO_VAL, GPIO_P12V_STBY_SLOT1_EN); if (write_device(vpath, "1")) {//XG1 is low to enable MB_P12V_STBY return -1; } pal_baseboard_clock_control(slot_id, "1"); return 0; } int pal_system_config_check(uint8_t slot_id) { char vpath[80] = {0}; char cmd[80] = {0}; int slot_type = -1; int last_slot_type = -1; char slot_str[80] = {0}; char last_slot_str[80] = {0}; // 0(Server), 1(Crane Flat), 2(Glacier Point), 3(Empty Slot) slot_type = minilaketb_get_slot_type(slot_id); switch (slot_type) { case SLOT_TYPE_SERVER: sprintf(slot_str,"1S Server"); break; case SLOT_TYPE_CF: sprintf(slot_str,"Crane Flat"); break; case SLOT_TYPE_GP: sprintf(slot_str,"Glacier Point"); break; case SLOT_TYPE_NULL: sprintf(slot_str,"Empty Slot"); break; default: sprintf(slot_str,"Device is not in AVL"); break; } // Get last slot type sprintf(vpath,SLOT_RECORD_FILE,slot_id); if (read_device(vpath, &last_slot_type)) { printf("Get last slot type failed\n"); return -1; } // 0(Server), 1(Crane Flat), 2(Glacier Point), 3(Empty Slot) switch (last_slot_type) { case SLOT_TYPE_SERVER: sprintf(last_slot_str,"1S Server"); break; case SLOT_TYPE_CF: sprintf(last_slot_str,"Crane Flat"); break; case SLOT_TYPE_GP: sprintf(last_slot_str,"Glacier Point"); break; case SLOT_TYPE_NULL: sprintf(last_slot_str,"Empty Slot"); break; default: sprintf(last_slot_str,"Device is not in AVL"); break; } sprintf(cmd, "rm -f %s",vpath); system(cmd); if ( slot_type != last_slot_type) { syslog(LOG_CRIT, "Unexpected swap on SLOT%u from %s to %s",slot_id,last_slot_str,slot_str); } return 0; } // Control 12V to the server in a given slot static int server_12v_on(uint8_t slot_id) { char vpath[64] = {0}; int ret=-1; uint8_t slot_prsnt, slot_latch; int rc, pid_file; int retry = MAX_BIC_CHECK_RETRY; bic_gpio_t gpio; // Check if another hotservice-reinit.sh instance of slotX is running while(1) { pid_file = open(HOTSERVICE_PID, O_CREAT | O_RDWR, 0666); rc = flock(pid_file, LOCK_EX); if (rc) { printf("slot%d is waitting\n",slot_id); sleep(1); } else { break; } } if (slot_id != 1) { return -1; } ret = pal_is_fru_prsnt(slot_id, &slot_prsnt); if (ret < 0) { printf("%s pal_is_fru_prsnt failed for fru: %d\n", __func__, slot_id); return -1; } #if 0 // Delay 2 seconds to check if slot is inserted entirely sleep(2); sprintf(vpath, GPIO_VAL, gpio_slot_latch[slot_id]); if (read_device(vpath, &slot_latch)) { return -1; } #else slot_latch = 0; #endif // Reject 12V-on action when SLOT is not present or SLOT ejector latch pin is high if ((1 != slot_prsnt) || (slot_latch)) { flock(pid_file, LOCK_UN); close(pid_file); return -1; } // Write 12V on memset(vpath, 0, sizeof(vpath)); sprintf(vpath, GPIO_VAL, GPIO_P12V_STBY_SLOT1_EN); if (write_device(vpath, "0")) { //XG1 is low to enable MB_P12V_STBY return -1; } rc = flock(pid_file, LOCK_UN); close(pid_file); if (!pal_is_slot_server(slot_id)) return 0; // Wait for BIC ipmb interface is ready while (retry) { ret = bic_get_gpio(slot_id, &gpio); if (!ret) break; sleep(1); retry--; } if (ret) { syslog(LOG_INFO, "%s: bic_get_gpio returned error during 12V off to on for fru %d",__func__ ,slot_id); } return 0; } // Display the given POST code using GPIO port static int pal_post_display(uint8_t status) { char path[64] = {0}; int ret; char *val; #ifdef DEBUG syslog(LOG_WARNING, "pal_post_display: status is %d\n", status); #endif sprintf(path, GPIO_VAL, GPIO_POSTCODE_0); if (BIT(status, 0)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_1); if (BIT(status, 1)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_2); if (BIT(status, 2)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_3); if (BIT(status, 3)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_4); if (BIT(status, 4)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_5); if (BIT(status, 5)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_6); if (BIT(status, 6)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } sprintf(path, GPIO_VAL, GPIO_POSTCODE_7); if (BIT(status, 7)) { val = "1"; } else { val = "0"; } ret = write_device(path, val); if (ret) { goto post_exit; } post_exit: if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "write_device failed for %s\n", path); #endif return -1; } else { return 0; } } static int read_device_hex(const char *device, int *value) { FILE *fp; int rc; fp = fopen(device, "r"); if (!fp) { #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", device); #endif return errno; } rc = fscanf(fp, "%x", value); fclose(fp); if (rc != 1) { #ifdef DEBUG syslog(LOG_INFO, "failed to read device %s", device); #endif return ENOENT; } else { return 0; } } // Platform Abstraction Layer (PAL) Functions int pal_get_platform_name(char *name) { strcpy(name, MINILAKETB_PLATFORM_NAME); return 0; } int pal_get_num_slots(uint8_t *num) { *num = MINILAKETB_MAX_NUM_SLOTS; return 0; } int pal_is_test_board() { //Test Board:1, Non Test Board:0 return 1; //this is minilake test board } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { switch (fru) { case FRU_SLOT1: //XG1 doesn't have present pin *status = 1; case FRU_SPB: *status = 1; break; default: return -1; } return 0; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { int val=0; char path[64] = {0}; int ret=-1; switch (fru) { case FRU_SLOT1: switch(minilaketb_get_slot_type(fru)) { case SLOT_TYPE_SERVER: sprintf(path, GPIO_VAL, GPIO_I2C_SLOT1_ALERT_N); if (read_device(path, &val)) { return -1; } if (val == 0x0) { *status = 1; } else { *status = 0; } break; case SLOT_TYPE_CF: case SLOT_TYPE_GP: ret = pal_is_fru_prsnt(fru,status); if(ret < 0) return -1; /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(fru, (uint8_t *)&val); if (ret < 0) { syslog(LOG_ERR, "pal_get_server_power: pal_is_server_12v_on failed"); return -1; } if (1 != val) { *status = 0; } break; } break; case FRU_SPB: *status = 1; break; default: return -1; } return 0; } int pal_is_slot_server(uint8_t fru) { if( fru == FRU_SLOT1 ) { return 1; // only have server slot1 } else { return 0; } } int pal_is_debug_card_prsnt(uint8_t *status) { int val; char path[64] = {0}; sprintf(path, GPIO_VAL, GPIO_DBG_CARD_PRSNT); if (read_device(path, &val)) { return -1; } if (val == 0x0) { *status = 1; } else { *status = 0; } return 0; } int pal_get_server_power(uint8_t slot_id, uint8_t *status) { int ret, val; char value[MAX_VALUE_LEN]; bic_gpio_t gpio; uint8_t retry = MAX_READ_RETRY; /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(slot_id, status); if (ret < 0) { syslog(LOG_ERR, "pal_get_server_power: pal_is_server_12v_on failed"); return -1; } /* If 12V-off, return */ if (!(*status)) { *status = SERVER_12V_OFF; return 0; } if (!pal_is_slot_server(slot_id)) { sprintf(value, GPIO_VAL, GPIO_SLOT1_POWER_EN); if (!read_device(value, &val)) { *status = (val == 0x1) ? SERVER_POWER_ON : SERVER_POWER_OFF; } else { *status = SERVER_POWER_OFF; } return 0; } /* If 12V-on, check if the CPU is turned on or not */ while (retry) { ret = bic_get_gpio(slot_id, &gpio); if (!ret) break; msleep(50); retry--; } if (ret) { // Check for if the BIC is irresponsive due to 12V_OFF or 12V_CYCLE syslog(LOG_INFO, "pal_get_server_power: bic_get_gpio returned error hence" " reading the kv_store for last power state for fru %d", slot_id); pal_get_last_pwr_state(slot_id, value); if (!(strcmp(value, "off"))) { *status = SERVER_POWER_OFF; } else if (!(strcmp(value, "on"))) { *status = SERVER_POWER_ON; } else { return ret; } return 0; } if (gpio.pwrgood_cpu) { *status = SERVER_POWER_ON; } else { *status = SERVER_POWER_OFF; } return 0; } static int server_12v_cycle_physically(uint8_t slot_id){ if (server_12v_off(slot_id)) return -1; sleep(DELAY_12V_CYCLE); return (server_12v_on(slot_id)); } // Power Off, Power On, or Power Reset the server in given slot int pal_set_server_power(uint8_t slot_id, uint8_t cmd) { int ret; uint8_t status; bool gs_flag = false; if (slot_id < 1 || slot_id > 4) { return -1; } if ((cmd != SERVER_12V_OFF) && (cmd != SERVER_12V_ON) && (cmd != SERVER_12V_CYCLE)) { ret = pal_is_fru_ready(slot_id, &status); //Break out if fru is not ready if ((ret < 0) || (status == 0)) { return -2; } if (pal_get_server_power(slot_id, &status) < 0) { return -1; } } switch(cmd) { //avoid power control on GP and CF case SERVER_POWER_OFF: case SERVER_POWER_CYCLE: case SERVER_POWER_RESET: case SERVER_GRACEFUL_SHUTDOWN: case SERVER_POWER_ON: if(pal_is_slot_server(slot_id) == 0) { printf("Should not execute power on/off/graceful_shutdown/cycle/reset on device card\n"); return -2; } break; } switch(cmd) { case SERVER_POWER_ON: if (status == SERVER_POWER_ON) return 1; else return server_power_on(slot_id); break; case SERVER_POWER_OFF: if (status == SERVER_POWER_OFF) return 1; else return server_power_off(slot_id, gs_flag); break; case SERVER_POWER_CYCLE: if (status == SERVER_POWER_ON) { if (server_power_off(slot_id, gs_flag)) return -1; sleep(DELAY_POWER_CYCLE); return server_power_on(slot_id); } else if (status == SERVER_POWER_OFF) { return (server_power_on(slot_id)); } break; case SERVER_POWER_RESET: if (status == SERVER_POWER_ON) { ret = pal_set_rst_btn(slot_id, 0); if (ret < 0) return ret; msleep(100); //some server miss to detect a quick pulse, so delay 100ms between low high ret = pal_set_rst_btn(slot_id, 1); if (ret < 0) return ret; } else if (status == SERVER_POWER_OFF) { printf("Ignore to execute power reset action when the power status of server is off\n"); return -2; } break; case SERVER_GRACEFUL_SHUTDOWN: if (status == SERVER_POWER_OFF) { return 1; } else { gs_flag = true; return server_power_off(slot_id, gs_flag); } break; case SERVER_12V_ON: /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(slot_id, &status); if (ret < 0) { syslog(LOG_ERR, "pal_set_server_power: pal_is_server_12v_on failed"); return -1; } if(status) //Have already 12V-ON return 1; return server_12v_on(slot_id); case SERVER_12V_OFF: /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(slot_id, &status); if (ret < 0) { syslog(LOG_ERR, "pal_set_server_power: pal_is_server_12v_off failed"); return -1; } if (!status) //Have already 12V-OFF return 1; return server_12v_off(slot_id); case SERVER_12V_CYCLE: ret = server_12v_cycle_physically(slot_id); return ret; case SERVER_GLOBAL_RESET: return server_power_off(slot_id, false); default: return -1; } return 0; } int pal_get_fan_latch(uint8_t *status) { char path[64] = {0}; int val; sprintf(path, GPIO_VAL, GPIO_FAN_LATCH_DETECT); if (read_device(path, &val)) { return -1; } if (1 == val) *status = 1; else *status = 0; return 0; } int pal_sled_cycle(void) { return 0; } // Read the Front Panel Hand Switch and return the position int pal_get_hand_sw_physically(uint8_t *pos) { int gpio_fd; void *gpio_reg; uint8_t loc; if (!m_gpio_hand_sw) { gpio_fd = open("/dev/mem", O_RDWR|O_SYNC); if (gpio_fd < 0) { return -1; } gpio_reg = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, gpio_fd, AST_GPIO_BASE); m_gpio_hand_sw = (char*)gpio_reg + GPIO_AB_AA_Z_Y; close(gpio_fd); } loc = (((*(volatile uint32_t*) m_gpio_hand_sw) >> 20) & 0xF) % 5; // GPIOAA[7:4] switch(loc) { case 0: *pos = HAND_SW_SERVER1; break; default: *pos = HAND_SW_BMC; break; } return 0; } int pal_get_hand_sw(uint8_t *pos) { char value[MAX_VALUE_LEN]; uint8_t loc; int ret; ret = kv_get("spb_hand_sw", value, NULL, 0); if (!ret) { loc = atoi(value); if ((loc > HAND_SW_BMC) || (loc < HAND_SW_SERVER1)) { return pal_get_hand_sw_physically(pos); } *pos = loc; return 0; } return pal_get_hand_sw_physically(pos); } // Return the Front panel Power Button int pal_get_pwr_btn(uint8_t *status) { char path[64] = {0}; int val; sprintf(path, GPIO_VAL, GPIO_PWR_BTN); if (read_device(path, &val)) { return -1; } if (val) { *status = 0x0; } else { *status = 0x1; } return 0; } // Return the front panel's Reset Button status int pal_get_rst_btn(uint8_t *status) { char path[64] = {0}; int val; sprintf(path, GPIO_VAL, GPIO_RST_BTN); if (read_device(path, &val)) { return -1; } if (val) { *status = 0x0; } else { *status = 0x1; } return 0; } // Update the LED for the given slot with the status int pal_set_led(uint8_t fru, uint8_t status) { char path[64] = {0}; char *val; if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, GPIO_PWR1_LED); if (write_device(path, val)) { return -1; } return 0; } // Update Heartbeet LED int pal_set_hb_led(uint8_t status) { int vic_fd; void *vic_reg; void *vic_hb_mode; uint32_t ctrl; if (!m_hbled_output) { vic_fd = open("/dev/mem", O_RDWR|O_SYNC); if (vic_fd < 0) { return -1; } vic_reg = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, vic_fd, AST_VIC_BASE); vic_hb_mode = (char*)vic_reg + HW_HB_STATUS_OFFSET; m_hbled_output = (char*)vic_reg + HB_LED_OUTPUT_OFFSET; close(vic_fd); ctrl = *(volatile uint32_t*) vic_hb_mode; if (!(ctrl & SW_BLINK)) { ctrl |= SW_BLINK; *(volatile uint32_t*) vic_hb_mode = ctrl; } } *(volatile uint32_t*) m_hbled_output = status; return 0; } // Update the Identification LED for the given slot with the status int pal_set_id_led(uint8_t fru, uint8_t status) { char path[64] = {0}; char *val; if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, GPIO_SYSTEM_ID1_LED_N); if (write_device(path, val)) { return -1; } return 0; } // Update the VGA Mux to the server at given slot int pal_switch_vga_mux(uint8_t slot) { char *gpio_sw0, *gpio_sw1; char path[64] = {0}; // Based on the VGA mux table in Schematics switch(slot) { case HAND_SW_SERVER1: gpio_sw0 = "0"; gpio_sw1 = "0"; break; default: // default case, assumes server 1 gpio_sw0 = "0"; gpio_sw1 = "0"; break; } sprintf(path, GPIO_VAL, GPIO_VGA_SW0); if (write_device(path, gpio_sw0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "write_device failed for %s\n", path); #endif return -1; } sprintf(path, GPIO_VAL, GPIO_VGA_SW1); if (write_device(path, gpio_sw1) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "write_device failed for %s\n", path); #endif return -1; } return 0; } // Switch the UART mux to the given slot int pal_switch_uart_mux(uint8_t slot) { char * gpio_uart_sel0; char * gpio_uart_sel1; char * gpio_uart_sel2; char * gpio_uart_rx; char path[64] = {0}; uint8_t prsnt; int ret; ret = pal_is_debug_card_prsnt(&prsnt); if (ret) { goto uart_exit; } // Refer the UART select table in schematic switch(slot) { case HAND_SW_SERVER1: gpio_uart_sel2 = "0"; gpio_uart_sel1 = "0"; gpio_uart_sel0 = "0"; gpio_uart_rx = (prsnt) ? "0" : "1"; break; default: // for all other cases, assume BMC gpio_uart_sel2 = "1"; gpio_uart_sel1 = "0"; gpio_uart_sel0 = "0"; gpio_uart_rx = "1"; break; } // Enable Debug card path sprintf(path, GPIO_VAL, GPIO_UART_SEL2); ret = write_device(path, gpio_uart_sel2); if (ret) { goto uart_exit; } sprintf(path, GPIO_VAL, GPIO_UART_SEL1); ret = write_device(path, gpio_uart_sel1); if (ret) { goto uart_exit; } sprintf(path, GPIO_VAL, GPIO_UART_SEL0); ret = write_device(path, gpio_uart_sel0); if (ret) { goto uart_exit; } sprintf(path, GPIO_VAL, GPIO_UART_RX); ret = write_device(path, gpio_uart_rx); if (ret) { goto uart_exit; } uart_exit: if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "pal_switch_uart_mux: write_device failed: %s\n", path); #endif return ret; } else { return 0; } } // Enable POST buffer for the server in given slot int pal_post_enable(uint8_t slot) { int ret; bic_config_t config = {0}; bic_config_u *t = (bic_config_u *) &config; ret = bic_get_config(slot, &config); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "post_enable: bic_get_config failed for fru: %d\n", slot); #endif return ret; } if (0 == t->bits.post) { t->bits.post = 1; ret = bic_set_config(slot, &config); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "post_enable: bic_set_config failed\n"); #endif return ret; } } return 0; } // Disable POST buffer for the server in given slot int pal_post_disable(uint8_t slot) { int ret; bic_config_t config = {0}; bic_config_u *t = (bic_config_u *) &config; ret = bic_get_config(slot, &config); if (ret) { return ret; } t->bits.post = 0; ret = bic_set_config(slot, &config); if (ret) { return ret; } return 0; } // Get the last post code of the given slot int pal_post_get_last(uint8_t slot, uint8_t *status) { int ret; uint8_t buf[MAX_IPMB_RES_LEN] = {0x0}; uint8_t len; ret = bic_get_post_buf(slot, buf, &len); if (ret) { return ret; } // The post buffer is LIFO and the first byte gives the latest post code *status = buf[0]; return 0; } // Handle the received post code, for now display it on debug card int pal_post_handle(uint8_t slot, uint8_t status) { uint8_t prsnt, pos; int ret; // Check for debug card presence ret = pal_is_debug_card_prsnt(&prsnt); if (ret) { return ret; } // No debug card present, return if (!prsnt) { return 0; } // Get the hand switch position ret = pal_get_hand_sw(&pos); if (ret) { return ret; } // If the give server is not selected, return if (pos != slot) { return 0; } // Display the post code in the debug card ret = pal_post_display(status); if (ret) { return ret; } return 0; } int pal_get_fru_list(char *list) { strcpy(list, pal_fru_list); return 0; } int pal_get_fru_id(char *str, uint8_t *fru) { return minilaketb_common_fru_id(str, fru); } int pal_get_fru_name(uint8_t fru, char *name) { return minilaketb_common_fru_name(fru, name); } int pal_get_fru_sdr_path(uint8_t fru, char *path) { return minilaketb_sensor_sdr_path(fru, path); } int pal_get_fru_sensor_list(uint8_t fru, uint8_t **sensor_list, int *cnt) { switch(fru) { case FRU_SLOT1: switch(minilaketb_get_slot_type(fru)) { case SLOT_TYPE_SERVER: *sensor_list = (uint8_t *) bic_sensor_list; *cnt = bic_sensor_cnt; break; case SLOT_TYPE_CF: *sensor_list = (uint8_t *) dc_cf_sensor_list; *cnt = dc_cf_sensor_cnt; break; case SLOT_TYPE_GP: *sensor_list = (uint8_t *) dc_sensor_list; *cnt = dc_sensor_cnt; break; default: return -1; break; } break; case FRU_SPB: *sensor_list = (uint8_t *) spb_sensor_list; *cnt = spb_sensor_cnt; break; default: #ifdef DEBUG syslog(LOG_WARNING, "pal_get_fru_sensor_list: Wrong fru id %u", fru); #endif return -1; } return 0; } int pal_fruid_write(uint8_t fru, char *path) { return bic_write_fruid(fru, 0, path); } int pal_sensor_sdr_init(uint8_t fru, sensor_info_t *sinfo) { uint8_t status; switch(fru) { case FRU_SLOT1: pal_is_fru_prsnt(fru, &status); break; case FRU_SPB: status = 1; break; } if (status) return minilaketb_sensor_sdr_init(fru, sinfo); else return -1; } static sensor_check_t * get_sensor_check(uint8_t fru, uint8_t snr_num) { if (fru < 1 || fru > MAX_NUM_FRUS) { syslog(LOG_WARNING, "get_sensor_check: Wrong FRU ID %d\n", fru); return NULL; } return &m_snr_chk[fru-1][snr_num]; } static sensor_desc_t * get_sensor_desc(uint8_t fru, uint8_t snr_num) { if (fru < 1 || fru > MAX_NUM_FRUS) { syslog(LOG_WARNING, "get_sensor_desc: Wrong FRU ID %d\n", fru); return NULL; } return &m_snr_desc[fru-1][snr_num]; } int pal_sensor_read_raw(uint8_t fru, uint8_t sensor_num, void *value) { uint8_t status; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; int ret; uint8_t val; uint8_t retry = MAX_READ_RETRY; sensor_check_t *snr_chk; bic_gpio_t gpio; switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sensor%d", fru, sensor_num); if(pal_is_fru_prsnt(fru, &status) < 0) return -1; if (!status) { return -1; } break; case FRU_SPB: sprintf(key, "spb_sensor%d", sensor_num); break; default: return -1; } snr_chk = get_sensor_check(fru, sensor_num); while (retry) { ret = minilaketb_sensor_read(fru, sensor_num, value); if ((ret >= 0) || (ret == EER_READ_NA)) break; msleep(50); retry--; } if(ret < 0) { if ((ret == EER_READ_NA) && snr_chk->val_valid) { snr_chk->val_valid = 0; return -1; } snr_chk->val_valid = 0; if (ret == EER_READ_NA) return ERR_SENSOR_NA; if(fru == FRU_SPB) return -1; if(pal_get_server_power(fru, &status) < 0) return -1; // This check helps interpret the IPMI packet loss scenario if(status == SERVER_POWER_ON) return -1; return ERR_SENSOR_NA; } else { // On successful sensor read if (fru == FRU_SPB) { if (sensor_num == SP_SENSOR_HSC_OUT_CURR) { power_value_adjust(curr_cali_table, (float *)value); } if (sensor_num == SP_SENSOR_HSC_IN_POWER) { power_value_adjust(pwr_cali_table, (float *)value); } if (sensor_num == SP_SENSOR_INLET_TEMP) { apply_inlet_correction((float *)value); } if ( sensor_num == SP_SENSOR_P12V_SLOT1 ) { /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(sensor_num - SP_SENSOR_P12V_SLOT1 + 1, &val); if (ret < 0) { syslog(LOG_ERR, "%s: pal_is_server_12v_on failed",__func__); } if (!val) { sprintf(str, "%.2f",*((float*)value)); kv_set(key, str, 0, 0); return -1; } } if ( sensor_num == SP_SENSOR_P3V3 ) { while (retry) { ret = bic_get_gpio(FRU_SLOT1, &gpio); if (!ret) break; sleep(1); retry--; } if (ret) { val = 0; } else { val = gpio.pwrgd_pch_pwrok; } if (val == 0) { sprintf(str, "%.2f",*((float*)value)); kv_set(key, str, 0, 0); return -1; } } } if ((GETBIT(snr_chk->flag, UCR_THRESH) && (*((float*)value) >= snr_chk->ucr)) || (GETBIT(snr_chk->flag, LCR_THRESH) && (*((float*)value) <= snr_chk->lcr))) { if (snr_chk->retry_cnt < MAX_CHECK_RETRY) { snr_chk->retry_cnt++; if (!snr_chk->val_valid) return -1; *((float*)value) = snr_chk->last_val; } } else { snr_chk->last_val = *((float*)value); snr_chk->val_valid = 1; snr_chk->retry_cnt = 0; } } return ret; } int pal_sensor_threshold_flag(uint8_t fru, uint8_t snr_num, uint16_t *flag) { uint8_t val; int ret; switch(fru) { case FRU_SLOT1: if (snr_num == BIC_SENSOR_SOC_THERM_MARGIN) *flag = GETMASK(SENSOR_VALID) | GETMASK(UCR_THRESH); else if (snr_num == BIC_SENSOR_SOC_PACKAGE_PWR) *flag = GETMASK(SENSOR_VALID); else if (snr_num == BIC_SENSOR_SOC_TJMAX) *flag = GETMASK(SENSOR_VALID); break; case FRU_SPB: /* * TODO: This is a HACK (t11229576) */ switch(snr_num) { case SP_SENSOR_P12V_SLOT1: /* Check whether the system is 12V off or on */ ret = pal_is_server_12v_on(snr_num - SP_SENSOR_P12V_SLOT1 + 1, &val); if (ret < 0) { syslog(LOG_ERR, "%s: pal_is_server_12v_on failed",__func__); } if (!val) { *flag = GETMASK(SENSOR_VALID); } break; } } return 0; } int pal_get_sensor_threshold(uint8_t fru, uint8_t sensor_num, uint8_t thresh, void *value) { return minilaketb_sensor_threshold(fru, sensor_num, thresh, value); } int pal_get_sensor_name(uint8_t fru, uint8_t sensor_num, char *name) { return minilaketb_sensor_name(fru, sensor_num, name); } int pal_get_sensor_units(uint8_t fru, uint8_t sensor_num, char *units) { return minilaketb_sensor_units(fru, sensor_num, units); } int pal_get_fruid_path(uint8_t fru, char *path) { return minilaketb_get_fruid_path(fru, path); } int pal_get_fruid_eeprom_path(uint8_t fru, char *path) { return minilaketb_get_fruid_eeprom_path(fru, path); } int pal_get_fruid_name(uint8_t fru, char *name) { return minilaketb_get_fruid_name(fru, name); } int pal_set_def_key_value() { int i, ret; int fru; char key[MAX_KEY_LEN]; for (i = 0; strcmp(key_list[i], LAST_KEY) != 0; i++) { if ((ret = kv_set(key_list[i], def_val_list[i], 0, KV_FPERSIST | KV_FCREATE)) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_def_key_value: kv_set failed. %d", ret); #endif } } /* Actions to be taken on Power On Reset */ if (pal_is_bmc_por()) { for (fru = 1; fru <= MAX_NUM_FRUS; fru++) { /* Clear all the SEL errors */ memset(key, 0, MAX_KEY_LEN); switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sel_error", fru); break; case FRU_SPB: continue; default: return -1; } /* Write the value "1" which means FRU_STATUS_GOOD */ ret = pal_set_key_value(key, "1"); /* Clear all the sensor health files*/ memset(key, 0, MAX_KEY_LEN); switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_SPB: continue; default: return -1; } /* Write the value "1" which means FRU_STATUS_GOOD */ ret = pal_set_key_value(key, "1"); } } return 0; } int pal_get_fru_devtty(uint8_t fru, char *devtty) { switch(fru) { case FRU_SLOT1: sprintf(devtty, "/dev/ttyS1"); break; default: #ifdef DEBUG syslog(LOG_WARNING, "pal_get_fru_devtty: Wrong fru id %u", fru); #endif return -1; } return 0; } void pal_dump_key_value(void) { int i = 0; int ret; char value[MAX_VALUE_LEN] = {0x0}; while (strcmp(key_list[i], LAST_KEY)) { printf("%s:", key_list[i]); if ((ret = kv_get(key_list[i], value, NULL, KV_FPERSIST)) < 0) { printf("\n"); } else { printf("%s\n", value); } i++; memset(value, 0, MAX_VALUE_LEN); } } int pal_set_last_pwr_state(uint8_t fru, char *state) { int ret; char key[MAX_KEY_LEN] = {0}; sprintf(key, "pwr_server%d_last_state", (int) fru); ret = pal_set_key_value(key, state); if (ret < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_last_pwr_state: pal_set_key_value failed for " "fru %u", fru); #endif } return ret; } int pal_get_last_pwr_state(uint8_t fru, char *state) { int ret; char key[MAX_KEY_LEN] = {0}; switch(fru) { case FRU_SLOT1: sprintf(key, "pwr_server%d_last_state", (int) fru); ret = pal_get_key_value(key, state); if (ret < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_get_last_pwr_state: pal_get_key_value failed for " "fru %u", fru); #endif } return ret; case FRU_SPB: sprintf(state, "on"); return 0; } return -1; } // Write GUID into EEPROM static int pal_set_guid(uint16_t offset, char *guid) { int fd = 0; ssize_t bytes_wr; errno = 0; // Check for file presence if (access(FRU_EEPROM, F_OK) == -1) { syslog(LOG_ERR, "pal_set_guid: unable to access the %s file: %s", FRU_EEPROM, strerror(errno)); return errno; } // Open file fd = open(FRU_EEPROM, O_WRONLY); if (fd == -1) { syslog(LOG_ERR, "pal_set_guid: unable to open the %s file: %s", FRU_EEPROM, strerror(errno)); return errno; } // Seek the offset lseek(fd, offset, SEEK_SET); // Write GUID data bytes_wr = write(fd, guid, GUID_SIZE); if (bytes_wr != GUID_SIZE) { syslog(LOG_ERR, "pal_set_guid: write to %s file failed: %s", FRU_EEPROM, strerror(errno)); goto err_exit; } err_exit: close(fd); return errno; } // Read GUID from EEPROM static int pal_get_guid(uint16_t offset, char *guid) { int fd = 0; ssize_t bytes_rd; errno = 0; // Check if file is present if (access(FRU_EEPROM, F_OK) == -1) { syslog(LOG_ERR, "pal_get_guid: unable to access the %s file: %s", FRU_EEPROM, strerror(errno)); return errno; } // Open the file fd = open(FRU_EEPROM, O_RDONLY); if (fd == -1) { syslog(LOG_ERR, "pal_get_guid: unable to open the %s file: %s", FRU_EEPROM, strerror(errno)); return errno; } // seek to the offset lseek(fd, offset, SEEK_SET); // Read bytes from location bytes_rd = read(fd, guid, GUID_SIZE); if (bytes_rd != GUID_SIZE) { syslog(LOG_ERR, "pal_get_guid: read to %s file failed: %s", FRU_EEPROM, strerror(errno)); goto err_exit; } err_exit: close(fd); return errno; } int pal_set_sys_guid(uint8_t slot, char *str) { uint8_t guid[GUID_SIZE] = {0x00}; pal_populate_guid(guid, str); return bic_set_sys_guid(slot, guid); } int pal_get_sys_guid(uint8_t slot, char *guid) { return bic_get_sys_guid(slot, (uint8_t *)guid); } int pal_set_sysfw_ver(uint8_t slot, uint8_t *ver) { int i; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; sprintf(key, "sysfw_ver_slot%d", (int) slot); for (i = 0; i < SIZE_SYSFW_VER; i++) { sprintf(tstr, "%02x", ver[i]); strcat(str, tstr); } return pal_set_key_value(key, str); } int pal_get_sysfw_ver(uint8_t slot, uint8_t *ver) { int i; int j = 0; int ret; int msb, lsb; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; sprintf(key, "sysfw_ver_slot%d", (int) slot); ret = pal_get_key_value(key, str); if (ret) { return ret; } for (i = 0; i < 2*SIZE_SYSFW_VER; i += 2) { sprintf(tstr, "%c\n", str[i]); msb = strtol(tstr, NULL, 16); sprintf(tstr, "%c\n", str[i+1]); lsb = strtol(tstr, NULL, 16); ver[j++] = (msb << 4) | lsb; } return 0; } int pal_get_80port_record(uint8_t slot, uint8_t *res_data, size_t max_len, size_t *res_len) { int ret; uint8_t status; uint8_t len; if (slot != FRU_SLOT1) { return PAL_ENOTSUP; } ret = pal_is_fru_prsnt(slot, &status); if (ret < 0) { return -1; } if (status == 0) { return PAL_ENOTREADY; } ret = pal_is_server_12v_on(slot, &status); if(ret < 0 || 0 == status) { return PAL_ENOTREADY; } if(!pal_is_slot_server(slot)) { return PAL_ENOTSUP; } // Send command to get 80 port record from Bridge IC ret = bic_request_post_buffer_data(slot, res_data, &len); if (ret == 0) *res_len = len; return ret; } int pal_is_bmc_por(void) { uint32_t scu_fd; uint32_t wdt; void *scu_reg; void *scu_wdt; scu_fd = open("/dev/mem", O_RDWR | O_SYNC ); if (scu_fd < 0) { return 0; } scu_reg = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, scu_fd, AST_SCU_BASE); scu_wdt = (char*)scu_reg + WDT_OFFSET; wdt = *(volatile uint32_t*) scu_wdt; munmap(scu_reg, PAGE_SIZE); close(scu_fd); if (wdt & 0x6) { return 0; } else { return 1; } } int pal_get_fru_discrete_list(uint8_t fru, uint8_t **sensor_list, int *cnt) { switch(fru) { case FRU_SLOT1: if (pal_is_slot_server(fru)) { *sensor_list = (uint8_t *) bic_discrete_list; *cnt = bic_discrete_cnt; } else { *sensor_list = NULL; *cnt = 0; } break; case FRU_SPB: *sensor_list = NULL; *cnt = 0; break; default: #ifdef DEBUG syslog(LOG_WARNING, "pal_get_fru_discrete_list: Wrong fru id %u", fru); #endif return -1; } return 0; } static void _print_sensor_discrete_log(uint8_t fru, uint8_t snr_num, char *snr_name, uint8_t val, char *event) { if (val) { syslog(LOG_CRIT, "ASSERT: %s discrete - raised - FRU: %d, num: 0x%X," " snr: %-16s val: %d", event, fru, snr_num, snr_name, val); } else { syslog(LOG_CRIT, "DEASSERT: %s discrete - settled - FRU: %d, num: 0x%X," " snr: %-16s val: %d", event, fru, snr_num, snr_name, val); } pal_update_ts_sled(); } int pal_sensor_discrete_check(uint8_t fru, uint8_t snr_num, char *snr_name, uint8_t o_val, uint8_t n_val) { char name[32], crisel[128]; bool valid = false; uint8_t diff = o_val ^ n_val; if (GETBIT(diff, 0)) { switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SOC_Thermal_Trip"); valid = true; sprintf(crisel, "%s - %s,FRU:%u", name, GETBIT(n_val, 0)?"ASSERT":"DEASSERT", fru); pal_add_cri_sel(crisel); break; case BIC_SENSOR_VR_HOT: sprintf(name, "SOC_VR_Hot"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 0), name); valid = false; } } if (GETBIT(diff, 1)) { switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SOC_FIVR_Fault"); valid = true; break; case BIC_SENSOR_VR_HOT: sprintf(name, "SOC_DIMM_AB_VR_Hot"); valid = true; break; case BIC_SENSOR_CPU_DIMM_HOT: sprintf(name, "SOC_MEMHOT"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 1), name); valid = false; } } if (GETBIT(diff, 2)) { switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: sprintf(name, "SYS_Throttle"); valid = true; break; case BIC_SENSOR_VR_HOT: sprintf(name, "SOC_DIMM_DE_VR_Hot"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 2), name); valid = false; } } if (GETBIT(diff, 4)) { if (snr_num == BIC_SENSOR_PROC_FAIL) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 4), "FRB3"); } } return 0; } static int pal_store_crashdump(uint8_t fru) { return minilaketb_common_crashdump(fru); } int pal_sel_handler(uint8_t fru, uint8_t snr_num, uint8_t *event_data) { char key[MAX_KEY_LEN] = {0}; char cvalue[MAX_VALUE_LEN] = {0}; static int assert_cnt[MINILAKETB_MAX_NUM_SLOTS] = {0}; /* For every SEL event received from the BIC, set the critical LED on */ switch(fru) { case FRU_SLOT1: switch(snr_num) { case CATERR_B: pal_store_crashdump(fru); break; case 0x00: // don't care sensor number 00h return 0; } sprintf(key, "slot%d_sel_error", fru); fru -= 1; if ((event_data[2] & 0x80) == 0) { // 0: Assertion, 1: Deassertion assert_cnt[fru]++; } else { if (--assert_cnt[fru] < 0) assert_cnt[fru] = 0; } sprintf(cvalue, "%s", (assert_cnt[fru] > 0) ? "0" : "1"); break; case FRU_SPB: return 0; default: return -1; } /* Write the value "0" which means FRU_STATUS_BAD */ return pal_set_key_value(key, cvalue); } int pal_get_event_sensor_name(uint8_t fru, uint8_t *sel, char *name) { uint8_t snr_type = sel[10]; uint8_t snr_num = sel[11]; // If SNR_TYPE is OS_BOOT, sensor name is OS switch (snr_type) { case OS_BOOT: // OS_BOOT used by OS sprintf(name, "OS"); return 0; default: if (minilaketb_sensor_name(fru, snr_num, name) != 0) { break; } return 0; } // Otherwise, translate it based on snr_num return pal_get_x86_event_sensor_name(fru, snr_num, name); } int pal_parse_sel(uint8_t fru, uint8_t *sel, char *error_log) { uint8_t snr_num = sel[11]; uint8_t *event_data = &sel[10]; uint8_t *ed = &event_data[3]; char temp_log[512] = {0}; uint8_t sen_type = event_data[0]; uint8_t chn_num, dimm_num; bool parsed = false; switch(snr_num) { case BIC_SENSOR_SYSTEM_STATUS: strcpy(error_log, ""); switch (ed[0] & 0x0F) { case 0x00: strcat(error_log, "SOC_Thermal_Trip"); break; case 0x01: strcat(error_log, "SOC_FIVR_Fault"); break; case 0x02: strcat(error_log, "SOC_Throttle"); break; case 0x03: strcat(error_log, "PCH_HOT"); break; } parsed = true; break; case BIC_SENSOR_CPU_DIMM_HOT: strcpy(error_log, ""); switch (ed[0] & 0x0F) { case 0x01: strcat(error_log, "SOC_MEMHOT"); break; } parsed = true; break; case MEMORY_ECC_ERR: case MEMORY_ERR_LOG_DIS: strcpy(error_log, ""); if (snr_num == MEMORY_ECC_ERR) { // SEL from MEMORY_ECC_ERR Sensor if ((ed[0] & 0x0F) == 0x0) { if (sen_type == 0x0C) { strcat(error_log, "Correctable"); sprintf(temp_log, "DIMM%02X ECC err", ed[2]); pal_add_cri_sel(temp_log); } else if (sen_type == 0x10) strcat(error_log, "Correctable ECC error Logging Disabled"); } else if ((ed[0] & 0x0F) == 0x1) { strcat(error_log, "Uncorrectable"); sprintf(temp_log, "DIMM%02X UECC err", ed[2]); pal_add_cri_sel(temp_log); } else if ((ed[0] & 0x0F) == 0x5) strcat(error_log, "Correctable ECC error Logging Limit Reached"); else strcat(error_log, "Unknown"); } else { // SEL from MEMORY_ERR_LOG_DIS Sensor if ((ed[0] & 0x0F) == 0x0) strcat(error_log, "Correctable Memory Error Logging Disabled"); else strcat(error_log, "Unknown"); } // DIMM number (ed[2]): // Bit[7:5]: Socket number (Range: 0-7) // Bit[4:3]: Channel number (Range: 0-3) // Bit[2:0]: DIMM number (Range: 0-7) if (((ed[1] & 0xC) >> 2) == 0x0) { /* All Info Valid */ chn_num = (ed[2] & 0x18) >> 3; dimm_num = ed[2] & 0x7; /* If critical SEL logging is available, do it */ if (sen_type == 0x0C) { if ((ed[0] & 0x0F) == 0x0) { sprintf(temp_log, "DIMM%c%d ECC err,FRU:%u", 'A'+chn_num, dimm_num, fru); pal_add_cri_sel(temp_log); } else if ((ed[0] & 0x0F) == 0x1) { sprintf(temp_log, "DIMM%c%d UECC err,FRU:%u", 'A'+chn_num, dimm_num, fru); pal_add_cri_sel(temp_log); } } /* Then continue parse the error into a string. */ /* All Info Valid */ sprintf(temp_log, " DIMM %c%d Logical Rank %d (CPU# %d, CHN# %d, DIMM# %d)", 'A'+chn_num, dimm_num, ed[1] & 0x03, (ed[2] & 0xE0) >> 5, chn_num, dimm_num); } else if (((ed[1] & 0xC) >> 2) == 0x1) { /* DIMM info not valid */ sprintf(temp_log, " (CPU# %d, CHN# %d)", (ed[2] & 0xE0) >> 5, (ed[2] & 0x18) >> 3); } else if (((ed[1] & 0xC) >> 2) == 0x2) { /* CHN info not valid */ sprintf(temp_log, " (CPU# %d, DIMM# %d)", (ed[2] & 0xE0) >> 5, ed[2] & 0x7); } else if (((ed[1] & 0xC) >> 2) == 0x3) { /* CPU info not valid */ sprintf(temp_log, " (CHN# %d, DIMM# %d)", (ed[2] & 0x18) >> 3, ed[2] & 0x7); } strcat(error_log, temp_log); parsed = true; break; } if (parsed == true) { if ((event_data[2] & 0x80) == 0) { strcat(error_log, " Assertion"); } else { strcat(error_log, " Deassertion"); } return 0; } pal_parse_sel_helper(fru, sel, error_log); return 0; } int pal_parse_oem_sel(uint8_t fru, uint8_t *sel, char *error_log) { char crisel[128]; uint8_t mfg_id[] = {0x4c, 0x1c, 0x00}; error_log[0] = '\0'; // Record Type: 0xC0 (OEM) if ((sel[2] == 0xC0) && !memcmp(&sel[7], mfg_id, sizeof(mfg_id))) { snprintf(crisel, sizeof(crisel), "Slot %u PCIe err,FRU:%u", sel[14], fru); pal_add_cri_sel(crisel); } return 0; } int pal_set_sensor_health(uint8_t fru, uint8_t value) { char key[MAX_KEY_LEN] = {0}; char cvalue[MAX_VALUE_LEN] = {0}; switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_SPB: sprintf(key, "spb_sensor_health"); break; default: return -1; } sprintf(cvalue, (value > 0) ? "1": "0"); return pal_set_key_value(key, cvalue); } int pal_get_fru_health(uint8_t fru, uint8_t *value) { char cvalue[MAX_VALUE_LEN] = {0}; char key[MAX_KEY_LEN] = {0}; int ret; switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_SPB: sprintf(key, "spb_sensor_health"); break; default: return -1; } ret = pal_get_key_value(key, cvalue); if (ret) { return ret; } *value = atoi(cvalue); memset(key, 0, MAX_KEY_LEN); memset(cvalue, 0, MAX_VALUE_LEN); switch(fru) { case FRU_SLOT1: sprintf(key, "slot%d_sel_error", fru); break; case FRU_SPB: return 0; default: return -1; } ret = pal_get_key_value(key, cvalue); if (ret) { return ret; } *value = *value & atoi(cvalue); return 0; } void pal_inform_bic_mode(uint8_t fru, uint8_t mode) { switch(mode) { case BIC_MODE_NORMAL: // Bridge IC entered normal mode // Inform BIOS that BMC is ready bic_set_gpio(fru, BMC_READY_N, 0); break; case BIC_MODE_UPDATE: // Bridge IC entered update mode // TODO: Might need to handle in future break; default: break; } } int pal_get_fan_name(uint8_t num, char *name) { switch(num) { case FAN_0: sprintf(name, "Fan 0"); break; case FAN_1: sprintf(name, "Fan 1"); break; default: return -1; } return 0; } static int write_fan_value(const int fan, const char *device, const int value) { char full_name[LARGEST_DEVICE_NAME]; char device_name[LARGEST_DEVICE_NAME]; char output_value[LARGEST_DEVICE_NAME]; snprintf(device_name, LARGEST_DEVICE_NAME, device, fan); snprintf(full_name, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name); snprintf(output_value, LARGEST_DEVICE_NAME, "%d", value); return write_device(full_name, output_value); } int pal_set_fan_speed(uint8_t fan, uint8_t pwm) { int unit; int ret; if (fan >= pal_pwm_cnt) { syslog(LOG_INFO, "pal_set_fan_speed: fan number is invalid - %d", fan); return -1; } // Convert the percentage to our 1/96th unit. unit = pwm * PWM_UNIT_MAX / 100; // For 0%, turn off the PWM entirely if (unit == 0) { ret = write_fan_value(fan, "pwm%d_en", 0); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } return 0; // For 100%, set falling and rising to the same value } else if (unit == PWM_UNIT_MAX) { unit = 0; } ret = write_fan_value(fan, "pwm%d_type", 0); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } ret = write_fan_value(fan, "pwm%d_rising", 0); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } ret = write_fan_value(fan, "pwm%d_falling", unit); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } ret = write_fan_value(fan, "pwm%d_en", 1); if (ret < 0) { syslog(LOG_INFO, "set_fan_speed: write_fan_value failed"); return -1; } return 0; } int pal_get_fan_speed(uint8_t fan, int *rpm) { int ret; float value; // Redirect FAN to sensor cache ret = sensor_cache_read(FRU_SPB, SP_SENSOR_FAN0_TACH + fan, &value); if (0 == ret) *rpm = (int) value; return ret; } void pal_update_ts_sled() { char key[MAX_KEY_LEN] = {0}; char tstr[MAX_VALUE_LEN] = {0}; struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); sprintf(tstr, "%ld", ts.tv_sec); sprintf(key, "timestamp_sled"); pal_set_key_value(key, tstr); } int pal_handle_dcmi(uint8_t fru, uint8_t *request, uint8_t req_len, uint8_t *response, uint8_t *rlen) { int ret; uint8_t rbuf[256] = {0x00}, len = 0; ret = bic_me_xmit(fru, request, req_len, rbuf, &len); if (ret || (len < 1)) { return -1; } if (rbuf[0] != 0x00) { return -1; } *rlen = len - 1; memcpy(response, &rbuf[1], *rlen); return 0; } void pal_log_clear(char *fru) { char key[MAX_KEY_LEN] = {0}; int i; if (!strcmp(fru, "slot1")) { pal_set_key_value("slot1_sensor_health", "1"); pal_set_key_value("slot1_sel_error", "1"); } else if (!strcmp(fru, "spb")) { pal_set_key_value("spb_sensor_health", "1"); } else if (!strcmp(fru, "all")) { for (i = 1; i <= 1; i++) { sprintf(key, "slot%d_sensor_health", i); pal_set_key_value(key, "1"); sprintf(key, "slot%d_sel_error", i); pal_set_key_value(key, "1"); } pal_set_key_value("spb_sensor_health", "1"); } } int pal_get_pwm_value(uint8_t fan_num, uint8_t *value) { char path[LARGEST_DEVICE_NAME] = {0}; char device_name[LARGEST_DEVICE_NAME] = {0}; int val = 0; int pwm_enable = 0; if(fan_num < 0 || fan_num >= pal_pwm_cnt) { syslog(LOG_INFO, "pal_get_pwm_value: fan number is invalid - %d", fan_num); return -1; } // Need check pwmX_en to determine the PWM is 0 or 100. snprintf(device_name, LARGEST_DEVICE_NAME, "pwm%d_en", fan_num); snprintf(path, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name); if (read_device(path, &pwm_enable)) { syslog(LOG_INFO, "pal_get_pwm_value: read %s failed", path); return -1; } if(pwm_enable) { snprintf(device_name, LARGEST_DEVICE_NAME, "pwm%d_falling", fan_num); snprintf(path, LARGEST_DEVICE_NAME, "%s/%s", PWM_DIR, device_name); if (read_device_hex(path, &val)) { syslog(LOG_INFO, "pal_get_pwm_value: read %s failed", path); return -1; } if(val == 0) *value = 100; else *value = (100 * val + (PWM_UNIT_MAX-1)) / PWM_UNIT_MAX; } else { *value = 0; } return 0; } int pal_fan_dead_handle(int fan_num) { // TODO: Add action in case of fan dead return 0; } int pal_fan_recovered_handle(int fan_num) { // TODO: Add action in case of fan recovered return 0; } int pal_get_board_id(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { int BOARD_ID, BOARD_REV_ID0, BOARD_REV_ID1, BOARD_REV_ID2, SLOT_TYPE; char path[64] = {0}; unsigned char *data = res_data; int completion_code = CC_UNSPECIFIED_ERROR; sprintf(path, GPIO_VAL, GPIO_BOARD_ID); if (read_device(path, &BOARD_ID)) { *res_len = 0; return completion_code; } sprintf(path, GPIO_VAL, GPIO_BOARD_REV_ID0); if (read_device(path, &BOARD_REV_ID0)) { *res_len = 0; return completion_code; } sprintf(path, GPIO_VAL, GPIO_BOARD_REV_ID1); if (read_device(path, &BOARD_REV_ID1)) { *res_len = 0; return completion_code; } sprintf(path, GPIO_VAL, GPIO_BOARD_REV_ID2); if (read_device(path, &BOARD_REV_ID2)) { *res_len = 0; return completion_code; } switch(minilaketb_get_slot_type(slot)) { case SLOT_TYPE_SERVER: SLOT_TYPE = 0x00; break; case SLOT_TYPE_CF: SLOT_TYPE = 0x02; break; case SLOT_TYPE_GP: SLOT_TYPE = 0x01; break; default : *res_len = 0; return completion_code; } *data++ = BOARD_ID; *data++ = (BOARD_REV_ID2 << 2) | (BOARD_REV_ID1 << 1) | BOARD_REV_ID0; *data++ = slot; *data++ = SLOT_TYPE; *res_len = data - res_data; completion_code = CC_SUCCESS; return completion_code; } int pal_get_board_rev_id(uint8_t *id) { return 0; } int pal_get_mb_slot_id(uint8_t *id) { return 0; } int pal_get_slot_cfg_id(uint8_t *id) { return 0; } int pal_get_boot_order(uint8_t slot, uint8_t *req_data, uint8_t *boot, uint8_t *res_len) { int i; int j = 0; int ret; int msb, lsb; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; sprintf(key, "slot%d_boot_order", slot); ret = pal_get_key_value(key, str); if (ret) { *res_len = 0; return ret; } for (i = 0; i < 2*SIZE_BOOT_ORDER; i += 2) { sprintf(tstr, "%c\n", str[i]); msb = strtol(tstr, NULL, 16); sprintf(tstr, "%c\n", str[i+1]); lsb = strtol(tstr, NULL, 16); boot[j++] = (msb << 4) | lsb; } *res_len = SIZE_BOOT_ORDER; return 0; } int pal_set_boot_order(uint8_t slot, uint8_t *boot, uint8_t *res_data, uint8_t *res_len) { int i, j, network_dev = 0; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; enum { BOOT_DEVICE_IPV4 = 0x1, BOOT_DEVICE_IPV6 = 0x9, }; *res_len = 0; for (i = 0; i < SIZE_BOOT_ORDER; i++) { if (i > 0) { // byte[0] is boot mode, byte[1:5] are boot order for (j = i+1; j < SIZE_BOOT_ORDER; j++) { if (boot[i] == boot[j]) return CC_INVALID_PARAM; } // If bit[2:0] is 001b (Network), bit[3] is IPv4/IPv6 order // bit[3]=0b: IPv4 first // bit[3]=1b: IPv6 first if ((boot[i] == BOOT_DEVICE_IPV4) || (boot[i] == BOOT_DEVICE_IPV6)) network_dev++; } snprintf(tstr, 3, "%02x", boot[i]); strncat(str, tstr, 3); } // not allow having more than 1 network boot device in the boot order if (network_dev > 1) return CC_INVALID_PARAM; sprintf(key, "slot%d_boot_order", slot); return pal_set_key_value(key, str); } int pal_set_dev_guid(uint8_t slot, char *guid) { pal_populate_guid(g_dev_guid, guid); return pal_set_guid(OFFSET_DEV_GUID, (char *)g_dev_guid); } int pal_get_dev_guid(uint8_t fru, char *guid) { pal_get_guid(OFFSET_DEV_GUID, (char *)g_dev_guid); memcpy(guid, g_dev_guid, GUID_SIZE); return 0; } void pal_get_chassis_status(uint8_t slot, uint8_t *req_data, uint8_t *res_data, uint8_t *res_len) { char key[MAX_KEY_LEN] = {0}; sprintf(key, "slot%d_por_cfg", slot); char buff[MAX_VALUE_LEN]; int policy = 3; uint8_t status, ret; unsigned char *data = res_data; // Platform Power Policy if (pal_get_key_value(key, buff) == 0) { if (!memcmp(buff, "off", strlen("off"))) policy = 0; else if (!memcmp(buff, "lps", strlen("lps"))) policy = 1; else if (!memcmp(buff, "on", strlen("on"))) policy = 2; else policy = 3; } // Current Power State ret = pal_get_server_power(slot, &status); if (ret >= 0) { *data++ = status | (policy << 5); } else { // load default syslog(LOG_WARNING, "ipmid: pal_get_server_power failed for slot1\n"); *data++ = 0x00 | (policy << 5); } *data++ = 0x00; // Last Power Event *data++ = 0x40; // Misc. Chassis Status *data++ = 0x00; // Front Panel Button Disable *res_len = data - res_data; } uint8_t pal_set_power_restore_policy(uint8_t slot, uint8_t *pwr_policy, uint8_t *res_data) { uint8_t completion_code; char key[MAX_KEY_LEN] = {0}; sprintf(key, "slot%d_por_cfg", slot); completion_code = CC_SUCCESS; // Fill response with default values unsigned char policy = *pwr_policy & 0x07; // Power restore policy switch (policy) { case 0: if (pal_set_key_value(key, "off") != 0) completion_code = CC_UNSPECIFIED_ERROR; break; case 1: if (pal_set_key_value(key, "lps") != 0) completion_code = CC_UNSPECIFIED_ERROR; break; case 2: if (pal_set_key_value(key, "on") != 0) completion_code = CC_UNSPECIFIED_ERROR; break; case 3: // no change (just get present policy support) break; default: completion_code = CC_PARAM_OUT_OF_RANGE; break; } return completion_code; } int pal_set_ppin_info(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; int i; int completion_code = CC_UNSPECIFIED_ERROR; *res_len = 0; sprintf(key, "slot%d_cpu_ppin", slot); for (i = 0; i < SIZE_CPU_PPIN; i++) { sprintf(tstr, "%02x", req_data[i]); strcat(str, tstr); } if (pal_set_key_value(key, str) != 0) return completion_code; completion_code = CC_SUCCESS; return completion_code; } // To get the platform sku int pal_get_sku(void){ int pal_sku = 0; // PAL_SKU[6:4] = {SCC_RMT_TYPE_0, SLOTID_0, SLOTID_1} // PAL_SKU[3:0] = {IOM_TYPE0, IOM_TYPE1, IOM_TYPE2, IOM_TYPE3} if (read_device(PLATFORM_FILE, &pal_sku)) { printf("Get platform SKU failed\n"); return -1; } return pal_sku; } //For OEM command "CMD_OEM_GET_PLAT_INFO" 0x7e int pal_get_plat_sku_id(void){ return 0x06; // XG1 110b: MiniPack } //Do slot ac cycle static void * slot_ac_cycle(void *ptr) { int slot = (int)ptr; char pwr_state[MAX_VALUE_LEN] = {0}; pthread_detach(pthread_self()); msleep(500); pal_get_last_pwr_state(slot, pwr_state); if (!pal_set_server_power(slot, SERVER_12V_CYCLE)) { syslog(LOG_CRIT, "SERVER_12V_CYCLE successful for FRU: %d", slot); pal_power_policy_control(slot, pwr_state); } m_slot_ac_start[slot-1] = false; pthread_exit(0); } int pal_slot_ac_cycle(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len){ uint8_t completion_code = CC_UNSPECIFIED_ERROR; uint8_t *data = req_data; int ret, slot_id = slot; pthread_t tid; *res_len = 0; if ((*data != 0x55) || (*(data+1) != 0x66) || (*(data+2) != 0x0f)) { return completion_code; } if (m_slot_ac_start[slot-1] != false) { return CC_NOT_SUPP_IN_CURR_STATE; } m_slot_ac_start[slot-1] = true; ret = pthread_create(&tid, NULL, slot_ac_cycle, (void *)slot_id); if (ret < 0) { syslog(LOG_WARNING, "[%s] Create Slot AC Cycle thread failed!\n", __func__); m_slot_ac_start[slot-1] = false; return CC_NODE_BUSY; } completion_code = CC_SUCCESS; return completion_code; } //Do sled ac cycle static void * sled_ac_cycle_handler(void *arg) { pthread_detach(pthread_self()); msleep(500); syslog(LOG_CRIT, "SLED_CYCLE starting..."); pal_update_ts_sled(); sync(); sleep(1); pal_sled_cycle(); m_sled_ac_start = false; pthread_exit(0); } int sled_ac_cycle(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len){ uint8_t completion_code = CC_UNSPECIFIED_ERROR; int ret; pthread_t tid; if (m_sled_ac_start != false) { return CC_NOT_SUPP_IN_CURR_STATE; } m_sled_ac_start = true; ret = pthread_create(&tid, NULL, sled_ac_cycle_handler, NULL); if (ret < 0) { syslog(LOG_WARNING, "[%s] Create Sled AC Cycle thread failed!\n", __func__); m_sled_ac_start = false; return CC_NODE_BUSY; } completion_code = CC_SUCCESS; return completion_code; } int pal_sled_ac_cycle(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len){ uint8_t completion_code = CC_UNSPECIFIED_ERROR; uint8_t *data = req_data; uint8_t slot_id = slot; *res_len = 0; if ((*data != 0x55) || (*(data+1) != 0x66)) { return completion_code; } switch(*(data+2)){ case 0x0f: //do slot ac cycle completion_code = pal_slot_ac_cycle(slot_id, req_data, req_len, res_data, res_len); return completion_code; break; case 0xac: //do sled ac cycle completion_code = sled_ac_cycle(slot_id, req_data, req_len, res_data, res_len); return completion_code; break; default: return completion_code; break; } completion_code = CC_SUCCESS; return completion_code; } //Use part of the function for OEM Command "CMD_OEM_GET_POSS_PCIE_CONFIG" 0xF4 int pal_get_poss_pcie_config(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t completion_code = CC_SUCCESS; uint8_t pcie_conf = 0x00; uint8_t *data = res_data; pcie_conf = 0x0A; // for XG1 test board *data++ = pcie_conf; *res_len = data - res_data; return completion_code; } int pal_set_imc_version(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t completion_code = CC_SUCCESS; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; int i; *res_len = 0; sprintf(key, "slot%d_imc_ver", (int)slot); for (i = 0; i < IMC_VER_SIZE; i++) { sprintf(tstr, "%x", req_data[i]); strcat(str, tstr); } if(kv_set(key, str, 0, KV_FPERSIST)) completion_code = CC_INVALID_PARAM; return completion_code; } int pal_set_slot_led(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t completion_code = CC_UNSPECIFIED_ERROR; uint8_t *data = req_data; int slot_id = slot; char tstr[64] = {0}; int ret=-1; *res_len = 0; sprintf(tstr, "identify_slot%d", slot_id); /* There are 2 option bytes for Chassis Identify Command * Byte 1 : Identify Interval in seconds. (Not support, OpenBMC only support turn off action) * 00h = Turn off Identify * Byte 2 : Force Identify On * BIT0 : 1b = Turn on Identify indefinitely. This overrides the values in byte 1. * 0b = Identify state driven according to byte 1. */ if (req_len <= 5) { if (5 == req_len) { if (GETBIT(*(data+1), 0)) { ret = pal_set_key_value(tstr, "on"); if (ret < 0) { syslog(LOG_ERR, "pal_set_key_value: set %s on failed",tstr); return completion_code; } } else if (0 == *data) { ret = pal_set_key_value(tstr, "off"); if (ret < 0) { syslog(LOG_ERR, "pal_set_key_value: set %s off failed",tstr); return completion_code; } } else { completion_code = CC_INVALID_PARAM; return completion_code; } } else if (4 == req_len) { if (0 == *data) { ret = pal_set_key_value(tstr, "off"); if (ret < 0) { syslog(LOG_ERR, "pal_set_key_value: set %s off failed",tstr); return completion_code; } } else { completion_code = CC_INVALID_PARAM; return completion_code; } } else { completion_code = CC_UNSPECIFIED_ERROR; return completion_code; } } else { completion_code = CC_PARAM_OUT_OF_RANGE; return completion_code; } completion_code = CC_SUCCESS; return completion_code; } int pal_get_platform_id(uint8_t *id) { return 0; } void pal_sensor_assert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh) { char crisel[128]; char thresh_name[8]; sensor_desc_t *snr_desc; switch (thresh) { case UNR_THRESH: sprintf(thresh_name, "UNR"); break; case UCR_THRESH: sprintf(thresh_name, "UCR"); break; case UNC_THRESH: sprintf(thresh_name, "UNCR"); break; case LNR_THRESH: sprintf(thresh_name, "LNR"); break; case LCR_THRESH: sprintf(thresh_name, "LCR"); break; case LNC_THRESH: sprintf(thresh_name, "LNCR"); break; default: syslog(LOG_WARNING, "pal_sensor_assert_handle: wrong thresh enum value"); return; } switch (snr_num) { case SP_SENSOR_FAN0_TACH: sprintf(crisel, "Fan0 %s %.0fRPM - ASSERT", thresh_name, val); break; case SP_SENSOR_FAN1_TACH: sprintf(crisel, "Fan1 %s %.0fRPM - ASSERT", thresh_name, val); break; case BIC_SENSOR_SOC_TEMP: sprintf(crisel, "SOC Temp %s %.0fC - ASSERT,FRU:%u", thresh_name, val, fru); break; case SP_SENSOR_P1V15_BMC_STBY: sprintf(crisel, "SP_P1V15_STBY %s %.2fV - ASSERT", thresh_name, val); break; case SP_SENSOR_P1V2_BMC_STBY: sprintf(crisel, "SP_P1V2_STBY %s %.2fV - ASSERT", thresh_name, val); break; case SP_SENSOR_P2V5_BMC_STBY: sprintf(crisel, "SP_P2V5_STBY %s %.2fV - ASSERT", thresh_name, val); break; case BIC_SENSOR_P3V3_MB: case BIC_SENSOR_P12V_MB: case BIC_SENSOR_P1V05_PCH: case BIC_SENSOR_P3V3_STBY_MB: case BIC_SENSOR_PV_BAT: case BIC_SENSOR_VCCIN_VR_VOL: case BIC_SENSOR_INA230_VOL: snr_desc = get_sensor_desc(fru, snr_num); sprintf(crisel, "%s %s %.2fV - ASSERT,FRU:%u", snr_desc->name, thresh_name, val, fru); break; case SP_SENSOR_P5V: case SP_SENSOR_P12V: case SP_SENSOR_P3V3_STBY: case SP_SENSOR_P12V_SLOT1: case SP_SENSOR_P3V3: case SP_P1V8_STBY: case SP_SENSOR_HSC_IN_VOLT: snr_desc = get_sensor_desc(FRU_SPB, snr_num); sprintf(crisel, "%s %s %.2fV - ASSERT", snr_desc->name, thresh_name, val); break; default: return; } pal_add_cri_sel(crisel); return; } void pal_sensor_deassert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh) { char crisel[128]; char thresh_name[8]; sensor_desc_t *snr_desc; switch (thresh) { case UNR_THRESH: sprintf(thresh_name, "UNR"); break; case UCR_THRESH: sprintf(thresh_name, "UCR"); break; case UNC_THRESH: sprintf(thresh_name, "UNCR"); break; case LNR_THRESH: sprintf(thresh_name, "LNR"); break; case LCR_THRESH: sprintf(thresh_name, "LCR"); break; case LNC_THRESH: sprintf(thresh_name, "LNCR"); break; default: syslog(LOG_WARNING, "pal_sensor_deassert_handle: wrong thresh enum value"); return; } switch (snr_num) { case SP_SENSOR_FAN0_TACH: sprintf(crisel, "Fan0 %s %.0fRPM - DEASSERT", thresh_name, val); break; case SP_SENSOR_FAN1_TACH: sprintf(crisel, "Fan1 %s %.0fRPM - DEASSERT", thresh_name, val); break; case BIC_SENSOR_SOC_TEMP: sprintf(crisel, "SOC Temp %s %.0fC - DEASSERT,FRU:%u", thresh_name, val, fru); break; case SP_SENSOR_P1V15_BMC_STBY: sprintf(crisel, "SP_P1V15_STBY %s %.2fV - DEASSERT", thresh_name, val); break; case SP_SENSOR_P1V2_BMC_STBY: sprintf(crisel, "SP_P1V2_STBY %s %.2fV - DEASSERT", thresh_name, val); break; case SP_SENSOR_P2V5_BMC_STBY: sprintf(crisel, "SP_P2V5_STBY %s %.2fV - DEASSERT", thresh_name, val); break; case BIC_SENSOR_P3V3_MB: case BIC_SENSOR_P12V_MB: case BIC_SENSOR_P1V05_PCH: case BIC_SENSOR_P3V3_STBY_MB: case BIC_SENSOR_PV_BAT: case BIC_SENSOR_VCCIN_VR_VOL: case BIC_SENSOR_INA230_VOL: snr_desc = get_sensor_desc(fru, snr_num); sprintf(crisel, "%s %s %.2fV - DEASSERT,FRU:%u", snr_desc->name, thresh_name, val, fru); break; case SP_SENSOR_P5V: case SP_SENSOR_P12V: case SP_SENSOR_P3V3_STBY: case SP_SENSOR_P12V_SLOT1: case SP_SENSOR_P3V3: case SP_P1V8_STBY: case SP_SENSOR_HSC_IN_VOLT: snr_desc = get_sensor_desc(FRU_SPB, snr_num); sprintf(crisel, "%s %s %.2fV - DEASSERT", snr_desc->name, thresh_name, val); break; default: return; } pal_add_cri_sel(crisel); return; } int pal_get_fw_info(uint8_t fru, unsigned char target, unsigned char* res, unsigned char* res_len) { return -1; } int pal_init_sensor_check(uint8_t fru, uint8_t snr_num, void *snr) { _sensor_thresh_t *psnr = (_sensor_thresh_t *)snr; sensor_check_t *snr_chk; sensor_desc_t *snr_desc; snr_chk = get_sensor_check(fru, snr_num); snr_chk->flag = psnr->flag; snr_chk->unr = psnr->unr; snr_chk->ucr = psnr->ucr; snr_chk->lcr = psnr->lcr; snr_chk->retry_cnt = 0; snr_chk->val_valid = 0; snr_chk->last_val = 0; snr_desc = get_sensor_desc(fru, snr_num); strncpy(snr_desc->name, psnr->name, sizeof(snr_desc->name)); snr_desc->name[sizeof(snr_desc->name)-1] = 0; return 0; } // TODO: Extend pal_get_status to support multiple servers // For now just, return the value of 'slot1_por_cfg' for all servers uint8_t pal_get_status(void) { char str_server_por_cfg[64]; char buff[MAX_VALUE_LEN]; int policy = 3; uint8_t data; // Platform Power Policy memset(str_server_por_cfg, 0 , sizeof(char) * 64); sprintf(str_server_por_cfg, "%s", "slot1_por_cfg"); if (pal_get_key_value(str_server_por_cfg, buff) == 0) { if (!memcmp(buff, "off", strlen("off"))) policy = 0; else if (!memcmp(buff, "lps", strlen("lps"))) policy = 1; else if (!memcmp(buff, "on", strlen("on"))) policy = 2; else policy = 3; } data = 0x01 | (policy << 5); return data; } // OEM Command "CMD_OEM_BYPASS_CMD" 0x34 int pal_bypass_cmd(uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len){ int ret; int completion_code=CC_UNSPECIFIED_ERROR; uint8_t netfn, cmd, select; uint8_t tlen, rlen; uint8_t tbuf[256] = {0x00}; uint8_t rbuf[256] = {0x00}; uint8_t status; if (slot != FRU_SLOT1) { return CC_PARAM_OUT_OF_RANGE; } ret = pal_is_fru_prsnt(slot, &status); if (ret < 0) { return -1; } if (status == 0) { return CC_UNSPECIFIED_ERROR; } ret = pal_is_server_12v_on(slot, &status); if(ret < 0 || 0 == status) { return CC_NOT_SUPP_IN_CURR_STATE; } if(!pal_is_slot_server(slot)) { return CC_UNSPECIFIED_ERROR; } select = req_data[0]; netfn = req_data[1]; cmd = req_data[2]; tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), data[1] (bypass netfn), data[2] (bypass cmd) if (tlen < 0) return completion_code; if (0 == select) { //BIC // Bypass command to Bridge IC if (tlen != 0) { ret = bic_ipmb_wrapper(slot, netfn, cmd, &req_data[3], tlen, res_data, res_len); } else { ret = bic_ipmb_wrapper(slot, netfn, cmd, NULL, 0, res_data, res_len); } if (0 == ret) completion_code = CC_SUCCESS; } else if (1 == select) { //ME tlen += 2; memcpy(tbuf, &req_data[1], tlen); tbuf[0] = tbuf[0] << 2; // Bypass command to ME ret = bic_me_xmit(slot, tbuf, tlen, rbuf, &rlen); if (0 == ret) { completion_code = rbuf[0]; memcpy(&res_data[0], &rbuf[1], (rlen - 1)); *res_len = rlen - 1; } } return completion_code; } unsigned char option_offset[] = {0,1,2,3,4,6,11,20,37,164}; unsigned char option_size[] = {1,1,1,1,2,5,9,17,127}; int pal_get_boot_option(unsigned char para,unsigned char* pbuff) { unsigned char size = option_size[para]; memset(pbuff, 0, size); return size; } int pal_handle_oem_1s_intr(uint8_t slot, uint8_t *data) { int sock; int err; struct sockaddr_un server; char sock_path[64] = {0}; #define SOCK_PATH_ASD_BIC "/tmp/asd_bic_socket" sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { err = errno; syslog(LOG_ERR, "%s failed open socket (errno=%d)", __FUNCTION__, err); return -1; } server.sun_family = AF_UNIX; sprintf(sock_path, "%s_%d", SOCK_PATH_ASD_BIC, slot); strcpy(server.sun_path, sock_path); if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) { err = errno; close(sock); syslog(LOG_ERR, "%s failed connecting stream socket (errno=%d), %s", __FUNCTION__, err, server.sun_path); return -1; } if (write(sock, data, 2) < 0) { err = errno; syslog(LOG_ERR, "%s error writing on stream sockets (errno=%d)", __FUNCTION__, err); } close(sock); return 0; } int pal_ipmb_processing(int bus, void *buf, uint16_t size) { char key[MAX_KEY_LEN]; char value[MAX_VALUE_LEN]; struct timespec ts; static time_t last_time = 0; if ((bus == 4) && (((uint8_t *)buf)[0] == 0x20)) { // OCP LCD debug card clock_gettime(CLOCK_MONOTONIC, &ts); if (ts.tv_sec >= (last_time + 5)) { last_time = ts.tv_sec; ts.tv_sec += 30; sprintf(key, "ocpdbg_lcd"); sprintf(value, "%ld", ts.tv_sec); if (kv_set(key, value, 0, 0) < 0) { return -1; } } } return 0; } int pal_is_mcu_ready(uint8_t bus) { char key[MAX_KEY_LEN]; char value[MAX_VALUE_LEN] = {0}; struct timespec ts; sprintf(key, "ocpdbg_lcd"); if (kv_get(key, value, NULL, 0)) { return false; } clock_gettime(CLOCK_MONOTONIC, &ts); if (strtoul(value, NULL, 10) > ts.tv_sec) { return true; } return false; } void pal_get_me_name(uint8_t fru, char *target_name) { strcpy(target_name, "ME"); return; } int pal_get_pcie_port_config (uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t pcie_port_config[SIZE_PCIE_PORT_CONFIG] = {0}; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[4] = {0}; int msb, lsb; int ret; int i; int j = 0; sprintf(key, "server_pcie_port_config"); ret = pal_get_key_value(key, str); if (ret) { *res_len = 0; return ret; } for (i = 0; i < 2 * SIZE_PCIE_PORT_CONFIG; i += 2) { sprintf(tstr, "%c\n", str[i]); msb = strtol(tstr, NULL, 16); sprintf(tstr, "%c\n", str[i+1]); lsb = strtol(tstr, NULL, 16); pcie_port_config[j] = (msb << 4) | lsb; j++; } memcpy(res_data, pcie_port_config, SIZE_PCIE_PORT_CONFIG); *res_len = SIZE_PCIE_PORT_CONFIG; return 0; } int pal_set_pcie_port_config (uint8_t slot, uint8_t *req_data, uint8_t req_len, uint8_t *res_data, uint8_t *res_len) { uint8_t pcie_port_config[SIZE_PCIE_PORT_CONFIG] = {0}; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; int i; *res_len = 0; sprintf(key, "server_pcie_port_config"); memcpy(pcie_port_config, req_data, SIZE_PCIE_PORT_CONFIG); for (i = 0; i < SIZE_PCIE_PORT_CONFIG; i++) { snprintf(tstr, 3, "%02x", pcie_port_config[i]); strncat(str, tstr, 3); } return pal_set_key_value(key, str); }