meta-facebook/meta-yosemite/recipes-yosemite/fblibs/files/pal/pal.c (2,319 lines of code) (raw):

/* * * Copyright 2015-present Facebook. All Rights Reserved. * * This file contains code to support IPMI2.0 Specification 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 <stdio.h> #include <stdint.h> #include <stdbool.h> #include <fcntl.h> #include <errno.h> #include <syslog.h> #include <sys/mman.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <openbmc/kv.h> #include "pal.h" #include "pal_sensors.h" #define BIT(value, index) ((value >> index) & 1) #define YOSEMITE_PLATFORM_NAME "Yosemite" #define LAST_KEY "last_key" #define YOSEMITE_MAX_NUM_SLOTS 4 #define GPIO_VAL "/sys/class/gpio/gpio%d/value" #define GPIO_DIR "/sys/class/gpio/gpio%d/direction" #define GPIO_HAND_SW_ID1 138 #define GPIO_HAND_SW_ID2 139 #define GPIO_HAND_SW_ID4 140 #define GPIO_HAND_SW_ID8 141 #define GPIO_RST_BTN 144 #define GPIO_PWR_BTN 24 #define GPIO_HB_LED 135 #define GPIO_USB_SW0 36 #define GPIO_USB_SW1 37 #define GPIO_USB_MUX_EN_N 147 #define GPIO_UART_SEL0 32 #define GPIO_UART_SEL1 33 #define GPIO_UART_SEL2 34 #define GPIO_UART_RX 35 #define GPIO_POSTCODE_0 48 #define GPIO_POSTCODE_1 49 #define GPIO_POSTCODE_2 50 #define GPIO_POSTCODE_3 51 #define GPIO_POSTCODE_4 124 #define GPIO_POSTCODE_5 125 #define GPIO_POSTCODE_6 126 #define GPIO_POSTCODE_7 127 #define GPIO_DBG_CARD_PRSNT 137 #define GPIO_BMC_READY_N 28 #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 DELAY_GRACEFUL_SHUTDOWN 1 #define DELAY_POWER_OFF 6 #define DELAY_POWER_CYCLE 10 #define DELAY_12V_CYCLE 5 #define CRASHDUMP_BIN "/usr/local/bin/dump.sh" #define CRASHDUMP_FILE "/mnt/data/crashdump_" #define LARGEST_DEVICE_NAME 120 #define PWM_DIR "/sys/devices/platform/ast_pwm_tacho.0" #define PWM_UNIT_MAX 96 #define MAX_READ_RETRY 10 #define MAX_CHECK_RETRY 2 #define CRASHDUMP_KEY "slot%d_crashdump" #define NUM_SERVER_FRU 4 #define NUM_NIC_FRU 1 #define NUM_BMC_FRU 1 const static uint8_t gpio_rst_btn[] = { 0, 57, 56, 59, 58 }; const static uint8_t gpio_led[] = { 0, 97, 96, 99, 98 }; const static uint8_t gpio_id_led[] = { 0, 41, 40, 43, 42 }; const static uint8_t gpio_prsnt[] = { 0, 61, 60, 63, 62 }; const static uint8_t gpio_bic_ready[] = { 0, 107, 106, 109, 108 }; const static uint8_t gpio_power[] = { 0, 27, 25, 31, 29 }; const static uint8_t gpio_12v[] = { 0, 117, 116, 119, 118 }; const char pal_fru_list[] = "all, slot1, slot2, slot3, slot4, spb, nic"; const char pal_server_list[] = "slot1, slot2, slot3, slot4"; 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"; typedef struct { uint16_t flag; float ucr; float unc; float unr; float lcr; float lnc; float lnr; } _sensor_thresh_t; typedef struct { uint16_t flag; 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}; char * key_list[] = { "pwr_server1_last_state", "pwr_server2_last_state", "pwr_server3_last_state", "pwr_server4_last_state", "sysfw_ver_slot1", "sysfw_ver_slot2", "sysfw_ver_slot3", "sysfw_ver_slot4", "identify_sled", "identify_slot1", "identify_slot2", "identify_slot3", "identify_slot4", "timestamp_sled", "slot1_por_cfg", "slot2_por_cfg", "slot3_por_cfg", "slot4_por_cfg", "slot1_sensor_health", "slot2_sensor_health", "slot3_sensor_health", "slot4_sensor_health", "spb_sensor_health", "nic_sensor_health", "slot1_sel_error", "slot2_sel_error", "slot3_sel_error", "slot4_sel_error", "slot1_boot_order", "slot2_boot_order", "slot3_boot_order", "slot4_boot_order", /* Add more Keys here */ LAST_KEY /* This is the last key of the list */ }; char * def_val_list[] = { "on", /* pwr_server1_last_state */ "on", /* pwr_server2_last_state */ "on", /* pwr_server3_last_state */ "on", /* pwr_server4_last_state */ "0", /* sysfw_ver_slot1 */ "0", /* sysfw_ver_slot2 */ "0", /* sysfw_ver_slot3 */ "0", /* sysfw_ver_slot4 */ "off", /* identify_sled */ "off", /* identify_slot1 */ "off", /* identify_slot2 */ "off", /* identify_slot3 */ "off", /* identify_slot4 */ "0", /* timestamp_sled */ "lps", /* slot1_por_cfg */ "lps", /* slot2_por_cfg */ "lps", /* slot3_por_cfg */ "lps", /* slot4_por_cfg */ "1", /* slot1_sensor_health */ "1", /* slot2_sensor_health */ "1", /* slot3_sensor_health */ "1", /* slot4_sensor_health */ "1", /* spb_sensor_health */ "1", /* nic_sensor_health */ "1", /* slot1_sel_error */ "1", /* slot2_sel_error */ "1", /* slot3_sel_error */ "1", /* slot4_sel_error */ "000000000000", /* slot1_boot_order */ "000000000000", /* slot2_boot_order */ "000000000000", /* slot3_boot_order */ "000000000000", /* slot4_boot_order */ /* Add more def values for the correspoding keys*/ LAST_KEY /* Same as last entry of the key_list */ }; struct power_coeff { float ein; float coeff; }; /* Quanta BMC correction table */ struct power_coeff power_table[] = { {51.0, 0.98}, {115.0, 0.9775}, {178.0, 0.9755}, {228.0, 0.979}, {290.0, 0.98}, {353.0, 0.977}, {427.0, 0.977}, {476.0, 0.9765}, {526.0, 0.9745}, {598.0, 0.9745}, {0.0, 0.0} }; /* Adjust power value */ static void power_value_adjust(float *value) { float x0, x1, y0, y1, x; int i; x = *value; x0 = power_table[0].ein; y0 = power_table[0].coeff; if (x0 > *value) { *value = x * y0; return; } for (i = 0; power_table[i].ein > 0.0; i++) { if (*value < power_table[i].ein) break; x0 = power_table[i].ein; y0 = power_table[i].coeff; } if (power_table[i].ein <= 0.0) { *value = x * y0; return; } //if value is bwtween x0 and x1, use linear interpolation method. x1 = power_table[i].ein; y1 = power_table[i].coeff; *value = (y0 + (((y1 - y0)/(x1 - x0)) * (x - x0))) * x; return; } typedef struct _inlet_corr_t { uint8_t duty; float delta_t; } inlet_corr_t; static inlet_corr_t g_ict[] = { // Inlet Sensor: // duty cycle vs delta_t { 10, 2.0 }, { 16, 1.5 }, { 22, 1.0 }, { 26, 0 }, }; static uint8_t g_ict_count = sizeof(g_ict)/sizeof(inlet_corr_t); static void apply_inlet_correction(float *value) { static float 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 = 1; 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); } // Power On the server in a given slot static int server_power_on(uint8_t slot_id) { char vpath[64] = {0}; sprintf(vpath, GPIO_VAL, gpio_power[slot_id]); if (write_device(vpath, "1")) { return -1; } if (write_device(vpath, "0")) { return -1; } sleep(1); if (write_device(vpath, "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 || slot_id > 4) { return -1; } sprintf(vpath, GPIO_VAL, gpio_power[slot_id]); 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; } // Control 12V to the server in a given slot static int server_12v_on(uint8_t slot_id) { char vpath[64] = {0}; int val; if (slot_id < 1 || slot_id > 4) { return -1; } sprintf(vpath, GPIO_VAL, gpio_12v[slot_id]); if (read_device(vpath, &val)) { return -1; } if (val == 0x1) { return 1; } if (write_device(vpath, "1")) { 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}; int val; if (slot_id < 1 || slot_id > 4) { return -1; } sprintf(vpath, GPIO_VAL, gpio_12v[slot_id]); if (read_device(vpath, &val)) { return -1; } if (val == 0x0) { return 1; } if (write_device(vpath, "0")) { return -1; } return 0; } // Debug Card's UART and BMC/SoL port share UART port and need to enable only // one TXD i.e. either BMC's TXD or Debug Port's TXD. static int control_sol_txd(uint8_t slot) { uint32_t scu_fd; uint32_t ctrl; void *scu_reg; void *scu_pin_ctrl1; void *scu_pin_ctrl2; scu_fd = open("/dev/mem", O_RDWR | O_SYNC ); if (scu_fd < 0) { #ifdef DEBUG syslog(LOG_WARNING, "control_sol_txd: open fails\n"); #endif return -1; } scu_reg = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, scu_fd, AST_SCU_BASE); scu_pin_ctrl1 = (char*)scu_reg + PIN_CTRL1_OFFSET; scu_pin_ctrl2 = (char*)scu_reg + PIN_CTRL2_OFFSET; switch(slot) { case 1: // Disable UART2's TXD and enable others ctrl = *(volatile uint32_t*) scu_pin_ctrl2; ctrl |= UART1_TXD; ctrl &= (~UART2_TXD); //Disable *(volatile uint32_t*) scu_pin_ctrl2 = ctrl; ctrl = *(volatile uint32_t*) scu_pin_ctrl1; ctrl |= UART3_TXD | UART4_TXD; *(volatile uint32_t*) scu_pin_ctrl1 = ctrl; break; case 2: // Disable UART1's TXD and enable others ctrl = *(volatile uint32_t*) scu_pin_ctrl2; ctrl &= (~UART1_TXD); // Disable ctrl |= UART2_TXD; *(volatile uint32_t*) scu_pin_ctrl2 = ctrl; ctrl = *(volatile uint32_t*) scu_pin_ctrl1; ctrl |= UART3_TXD | UART4_TXD; *(volatile uint32_t*) scu_pin_ctrl1 = ctrl; break; case 3: // Disable UART4's TXD and enable others ctrl = *(volatile uint32_t*) scu_pin_ctrl2; ctrl |= UART1_TXD | UART2_TXD; *(volatile uint32_t*) scu_pin_ctrl2 = ctrl; ctrl = *(volatile uint32_t*) scu_pin_ctrl1; ctrl |= UART3_TXD; ctrl &= (~UART4_TXD); // Disable *(volatile uint32_t*) scu_pin_ctrl1 = ctrl; break; case 4: // Disable UART3's TXD and enable others ctrl = *(volatile uint32_t*) scu_pin_ctrl2; ctrl |= UART1_TXD | UART2_TXD; *(volatile uint32_t*) scu_pin_ctrl2 = ctrl; ctrl = *(volatile uint32_t*) scu_pin_ctrl1; ctrl &= (~UART3_TXD); // Disable ctrl |= UART4_TXD; *(volatile uint32_t*) scu_pin_ctrl1 = ctrl; break; default: // Any other slots we need to enable all TXDs ctrl = *(volatile uint32_t*) scu_pin_ctrl2; ctrl |= UART1_TXD | UART2_TXD; *(volatile uint32_t*) scu_pin_ctrl2 = ctrl; ctrl = *(volatile uint32_t*) scu_pin_ctrl1; ctrl |= UART3_TXD | UART4_TXD; *(volatile uint32_t*) scu_pin_ctrl1 = ctrl; break; } munmap(scu_reg, PAGE_SIZE); close(scu_fd); 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, YOSEMITE_PLATFORM_NAME); return 0; } int pal_get_num_slots(uint8_t *num) { *num = YOSEMITE_MAX_NUM_SLOTS; return 0; } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { int val; char path[64] = {0}; switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(path, GPIO_VAL, gpio_prsnt[fru]); if (read_device(path, &val)) { return -1; } if (val == 0x0) { *status = 1; } else { *status = 0; } break; case FRU_SPB: case FRU_NIC: *status = 1; break; default: return -1; } return 0; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { int val; char path[64] = {0}; switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(path, GPIO_VAL, gpio_bic_ready[fru]); if (read_device(path, &val)) { return -1; } if (val == 0x0) { *status = 1; } else { *status = 0; } break; case FRU_SPB: case FRU_NIC: *status = 1; break; default: return -1; } return 0; } int pal_is_slot_server(uint8_t fru) { int ret = 0; switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: ret = 1; break; } return ret; } int pal_is_server_12v_on(uint8_t slot_id, uint8_t *status) { int val; char path[64] = {0}; if (slot_id < 1 || slot_id > 4) { return -1; } sprintf(path, GPIO_VAL, gpio_12v[slot_id]); if (read_device(path, &val)) { return -1; } if (val == 0x1) { *status = 1; } else { *status = 0; } 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; bic_gpio_t gpio; uint8_t retry = MAX_READ_RETRY; static uint8_t last_status[MAX_NODES+1] = {0}; /* 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; last_status[slot_id] = 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" " using the static last status %u for fru %d", last_status[slot_id], slot_id); *status = last_status[slot_id]; return 0; } if (gpio.pwrgood_cpu) { *status = SERVER_POWER_ON; } else { *status = SERVER_POWER_OFF; } last_status[slot_id] = *status; return 0; } // 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) { 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: return server_12v_on(slot_id); case SERVER_12V_OFF: return server_12v_off(slot_id); case SERVER_12V_CYCLE: if (server_12v_off(slot_id) < 0) { return -1; } sleep(DELAY_12V_CYCLE); return (server_12v_on(slot_id)); case SERVER_GLOBAL_RESET: return server_power_off(slot_id, false); default: return -1; } return 0; } int pal_sled_cycle(void) { syslog(LOG_CRIT, "SLED_CYCLE successful"); pal_update_ts_sled(); // Remove the adm1275 module as the HSC device is busy system("rmmod adm1275"); // Send command to HSC power cycle system("i2cset -y 10 0x40 0xd9 c"); return 0; } // Read the Front Panel Hand Switch and return the position int pal_get_hand_sw(uint8_t *pos) { char path[64] = {0}; int id1, id2, id4, id8; uint8_t loc; // Read 4 GPIOs to read the current position // id1: GPIOR2(138) // id2: GPIOR3(139) // id4: GPIOR4(140) // id8: GPIOR5(141) // Read ID1 sprintf(path, GPIO_VAL, GPIO_HAND_SW_ID1); if (read_device(path, &id1)) { return -1; } // Read ID2 sprintf(path, GPIO_VAL, GPIO_HAND_SW_ID2); if (read_device(path, &id2)) { return -1; } // Read ID4 sprintf(path, GPIO_VAL, GPIO_HAND_SW_ID4); if (read_device(path, &id4)) { return -1; } // Read ID8 sprintf(path, GPIO_VAL, GPIO_HAND_SW_ID8); if (read_device(path, &id8)) { return -1; } loc = ((id8 << 3) | (id4 << 2) | (id2 << 1) | (id1)); switch(loc) { case 0: case 5: *pos = HAND_SW_SERVER1; break; case 1: case 6: *pos = HAND_SW_SERVER2; break; case 2: case 7: *pos = HAND_SW_SERVER3; break; case 3: case 8: *pos = HAND_SW_SERVER4; break; default: *pos = HAND_SW_BMC; break; } return 0; } // 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 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 || slot > 4) { return -1; } if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, gpio_rst_btn[slot]); if (write_device(path, val)) { return -1; } return 0; } // Update the LED for the given slot with the status int pal_set_led(uint8_t slot, uint8_t status) { char path[64] = {0}; char *val; if (slot < 1 || slot > 4) { return -1; } if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, gpio_led[slot]); if (write_device(path, val)) { return -1; } return 0; } // Update Heartbeet LED int pal_set_hb_led(uint8_t status) { char path[64] = {0}; char *val; if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, GPIO_HB_LED); if (write_device(path, val)) { return -1; } return 0; } // Update the Identification LED for the given slot with the status int pal_set_id_led(uint8_t slot, uint8_t status) { char path[64] = {0}; char *val; if (slot < 1 || slot > 4) { return -1; } if (status) { val = "1"; } else { val = "0"; } sprintf(path, GPIO_VAL, gpio_id_led[slot]); if (write_device(path, val)) { return -1; } return 0; } static int set_usb_mux(uint8_t state) { int val; char *new_state; char path[64] = {0}; sprintf(path, GPIO_VAL, GPIO_USB_MUX_EN_N); if (read_device(path, &val)) { return -1; } // This GPIO Pin is active low if ((!val) == state) return 0; if (state) new_state = "0"; else new_state = "1"; if (write_device(path, new_state) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "write_device failed for %s\n", path); #endif return -1; } return 0; } // Update the USB Mux to the server at given slot int pal_switch_usb_mux(uint8_t slot) { char *gpio_sw0, *gpio_sw1; char path[64] = {0}; // Based on the USB mux table in Schematics switch(slot) { case HAND_SW_SERVER1: gpio_sw0 = "1"; gpio_sw1 = "0"; break; case HAND_SW_SERVER2: gpio_sw0 = "0"; gpio_sw1 = "0"; break; case HAND_SW_SERVER3: gpio_sw0 = "1"; gpio_sw1 = "1"; break; case HAND_SW_SERVER4: gpio_sw0 = "0"; gpio_sw1 = "1"; break; case HAND_SW_BMC: // Disable the USB MUX if (set_usb_mux(USB_MUX_OFF) < 0) return -1; else return 0; default: return 0; } // Enable the USB MUX if (set_usb_mux(USB_MUX_ON) < 0) return -1; sprintf(path, GPIO_VAL, GPIO_USB_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_USB_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}; int ret; // Refer the UART select table in schematic switch(slot) { case HAND_SW_SERVER1: gpio_uart_sel2 = "0"; gpio_uart_sel1 = "0"; gpio_uart_sel0 = "1"; gpio_uart_rx = "0"; break; case HAND_SW_SERVER2: gpio_uart_sel2 = "0"; gpio_uart_sel1 = "0"; gpio_uart_sel0 = "0"; gpio_uart_rx = "0"; break; case HAND_SW_SERVER3: gpio_uart_sel2 = "0"; gpio_uart_sel1 = "1"; gpio_uart_sel0 = "1"; gpio_uart_rx = "0"; break; case HAND_SW_SERVER4: gpio_uart_sel2 = "0"; gpio_uart_sel1 = "1"; gpio_uart_sel0 = "0"; gpio_uart_rx = "0"; 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; } // Diable TXD path from BMC to avoid conflict with SoL ret = control_sol_txd(slot); if (ret) { goto uart_exit; } // 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; } 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_capability(uint8_t fru, unsigned int *caps) { int ret = 0; switch (fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_SERVER | FRU_CAPABILITY_POWER_ALL | FRU_CAPABILITY_POWER_12V_ALL; break; case FRU_BMC: case FRU_SPB: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_MANAGEMENT_CONTROLLER; break; case FRU_NIC: *caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL | FRU_CAPABILITY_NETWORK_CARD; break; default: ret = -1; break; } return ret; } int pal_get_fru_id(char *str, uint8_t *fru) { return yosemite_common_fru_id(str, fru); } int pal_get_fru_name(uint8_t fru, char *name) { return yosemite_common_fru_name(fru, name); } int pal_get_fru_sdr_path(uint8_t fru, char *path) { return yosemite_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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: *sensor_list = (uint8_t *) bic_sensor_list; *cnt = bic_sensor_cnt; break; case FRU_SPB: *sensor_list = (uint8_t *) spb_sensor_list; *cnt = spb_sensor_cnt; break; case FRU_NIC: *sensor_list = (uint8_t *) nic_sensor_list; *cnt = nic_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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: pal_is_fru_prsnt(fru, &status); break; case FRU_SPB: case FRU_NIC: status = 1; break; } if (status) return yosemite_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]; } 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 retry = MAX_READ_RETRY; sensor_check_t *snr_chk; switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: 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; case FRU_NIC: sprintf(key, "nic_sensor%d", sensor_num); break; default: return -1; } snr_chk = get_sensor_check(fru, sensor_num); while (retry) { ret = yosemite_sensor_read(fru, sensor_num, value); if ((ret >= 0) || (ret == EER_UNHANDLED)) break; msleep(50); retry--; } if(ret < 0) { snr_chk->val_valid = 0; if (ret == EER_UNHANDLED) return -1; if(fru == FRU_SPB || fru == FRU_NIC) 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; strcpy(str, "NA"); } else { // On successful sensor read if (fru == FRU_SPB) { if (sensor_num == SP_SENSOR_INLET_TEMP) { apply_inlet_correction((float *)value); } else if (sensor_num == SP_SENSOR_HSC_IN_POWER) { power_value_adjust((float *)value); } } 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; } sprintf(str, "%.2f",*((float*)value)); } if(kv_set(key, str, 0, 0) < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_sensor_read_raw: cache_set key = %s, str = %s failed.", key, str); #endif return -1; } else { return ret; } } int pal_sensor_threshold_flag(uint8_t fru, uint8_t snr_num, uint16_t *flag) { switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: 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: case SP_SENSOR_P12V_SLOT2: case SP_SENSOR_P12V_SLOT3: case SP_SENSOR_P12V_SLOT4: *flag = GETMASK(SENSOR_VALID); break; } case FRU_NIC: break; } return 0; } int pal_get_sensor_threshold(uint8_t fru, uint8_t sensor_num, uint8_t thresh, void *value) { return yosemite_sensor_threshold(fru, sensor_num, thresh, value); } int pal_get_sensor_name(uint8_t fru, uint8_t sensor_num, char *name) { return yosemite_sensor_name(fru, sensor_num, name); } int pal_get_sensor_units(uint8_t fru, uint8_t sensor_num, char *units) { return yosemite_sensor_units(fru, sensor_num, units); } int pal_get_fruid_path(uint8_t fru, char *path) { return yosemite_get_fruid_path(fru, path); } int pal_get_fruid_eeprom_path(uint8_t fru, char *path) { return yosemite_get_fruid_eeprom_path(fru, path); } int pal_get_fruid_name(uint8_t fru, char *name) { return yosemite_get_fruid_name(fru, name); } int pal_set_def_key_value() { int ret; int i; int fru; char key[MAX_KEY_LEN] = {0}; 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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(key, "slot%d_sel_error", fru); break; case FRU_SPB: continue; case FRU_NIC: 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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_SPB: continue; case FRU_NIC: 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/ttyS2"); break; case FRU_SLOT2: sprintf(devtty, "/dev/ttyS1"); break; case FRU_SLOT3: sprintf(devtty, "/dev/ttyS4"); break; case FRU_SLOT4: sprintf(devtty, "/dev/ttyS3"); 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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: 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: case FRU_NIC: sprintf(state, "on"); return 0; } return 0; } 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_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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: *sensor_list = (uint8_t *) bic_discrete_list; *cnt = bic_discrete_cnt; break; case FRU_SPB: case FRU_NIC: *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]; 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; 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_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, "SOC_Throttle"); valid = true; break; } if (valid) { _print_sensor_discrete_log( fru, snr_num, snr_name, GETBIT(n_val, 2), name); valid = false; } } return 0; } static int pal_store_crashdump(uint8_t fru) { return yosemite_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[YOSEMITE_MAX_NUM_SLOTS] = {0}; /* For every SEL event received from the BIC, set the critical LED on */ switch(fru) { case FRU_SLOT1: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: switch(snr_num) { case CATERR_B: sprintf(key, CRASHDUMP_KEY, fru); kv_set(key, "1", 0, 0); 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; case FRU_NIC: return 0; default: return -1; } /* Write the value "0" which means FRU_STATUS_BAD */ return pal_set_key_value(key, cvalue); } 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; switch(snr_num) { 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); return 0; } pal_parse_sel_helper(fru, sel, error_log); 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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_SPB: sprintf(key, "spb_sensor_health"); break; case FRU_NIC: sprintf(key, "nic_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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(key, "slot%d_sensor_health", fru); break; case FRU_SPB: sprintf(key, "spb_sensor_health"); break; case FRU_NIC: sprintf(key, "nic_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: case FRU_SLOT2: case FRU_SLOT3: case FRU_SLOT4: sprintf(key, "slot%d_sel_error", fru); break; case FRU_SPB: return 0; case FRU_NIC: 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, GPIO_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, "%d", (int) 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) { return bic_me_xmit(fru, request, req_len, response, rlen); } void pal_log_clear(char *fru) { char key[MAX_KEY_LEN] = {0}; if (!strcmp(fru, "slot1")) { pal_set_key_value("slot1_sensor_health", "1"); pal_set_key_value("slot1_sel_error", "1"); } else if (!strcmp(fru, "slot2")) { pal_set_key_value("slot2_sensor_health", "1"); pal_set_key_value("slot2_sel_error", "1"); } else if (!strcmp(fru, "slot3")) { pal_set_key_value("slot3_sensor_health", "1"); pal_set_key_value("slot3_sel_error", "1"); } else if (!strcmp(fru, "slot4")) { pal_set_key_value("slot4_sensor_health", "1"); pal_set_key_value("slot4_sel_error", "1"); } else if (!strcmp(fru, "spb")) { pal_set_key_value("spb_sensor_health", "1"); } else if (!strcmp(fru, "nic")) { pal_set_key_value("nic_sensor_health", "1"); } else if (!strcmp(fru, "all")) { int i; for (i = 1; i <= 4; 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"); pal_set_key_value("nic_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_boot_order(uint8_t slot, uint8_t *req_data, uint8_t *boot, uint8_t *res_len) { int i, 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%u_boot_order", slot); ret = pal_get_key_value(key, str); if (ret) { *res_len = 0; return ret; } memset(boot, 0x00, SIZE_BOOT_ORDER); 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; char key[MAX_KEY_LEN] = {0}; char str[MAX_VALUE_LEN] = {0}; char tstr[10] = {0}; sprintf(key, "slot%u_boot_order", slot); for (i = 0; i < SIZE_BOOT_ORDER; i++) { snprintf(tstr, 3, "%02x", boot[i]); strncat(str, tstr, 3); } *res_len = 0; return pal_set_key_value(key, str); } int pal_is_crashdump_ongoing(uint8_t slot) { char key[MAX_KEY_LEN] = {0}; char value[MAX_VALUE_LEN] = {0}; int ret; sprintf(key, CRASHDUMP_KEY, slot); ret = kv_get(key, value, NULL, 0); if (ret < 0) { #ifdef DEBUG syslog(LOG_INFO, "pal_get_crashdumpe: failed"); #endif return 0; } if (atoi(value) > 0) return 1; return 0; } bool pal_is_fw_update_ongoing_system(void) { uint8_t i; for (i = FRU_SLOT1; i <= FRU_BMC; i++) { if (pal_is_fw_update_ongoing(i) == true) return true; } return false; } int pal_set_fw_update_ongoing(uint8_t fruid, uint16_t tmout) { char key[64] = {0}; char value[64] = {0}; struct timespec ts; if (fruid == FRU_BMC) { fruid = FRU_SPB; } sprintf(key, "fru%d_fwupd", fruid); clock_gettime(CLOCK_MONOTONIC, &ts); ts.tv_sec += tmout; sprintf(value, "%ld", ts.tv_sec); if (kv_set(key, value, 0, 0) < 0) { return -1; } return 0; } int pal_init_sensor_check(uint8_t fru, uint8_t snr_num, void *snr) { sensor_check_t *snr_chk; _sensor_thresh_t *psnr = (_sensor_thresh_t *)snr; snr_chk = get_sensor_check(fru, snr_num); snr_chk->flag = psnr->flag; 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; return 0; } void pal_sensor_assert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh) { } void pal_sensor_deassert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh) { } void pal_add_cri_sel(char *str) { } 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_set_dev_guid(uint8_t slot, char *guid) { return 0; } int pal_get_dev_guid(uint8_t fru, char *guid) { 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_get_platform_id(uint8_t *id) { return 0; } int pal_get_fw_info(uint8_t fru, unsigned char target, unsigned char* res, unsigned char* res_len) { return -1; } //For OEM command "CMD_OEM_GET_PLAT_INFO" 0x7e int pal_get_plat_sku_id(void){ return 0; // Yosemite V1 } int pal_force_update_bic_fw(uint8_t slot_id, uint8_t comp, char *path) { return bic_update_firmware(slot_id, comp, path, 1); } // 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; *res_len = 0; if (slot < FRU_SLOT1 || slot > FRU_SLOT4) { 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]; switch (select) { case BYPASS_BIC: tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), data[1] (bypass netfn), data[2] (bypass cmd) if (tlen < 0) { completion_code = CC_INVALID_LENGTH; break; } netfn = req_data[1]; cmd = req_data[2]; // 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; } break; case BYPASS_ME: tlen = req_len - 6; // payload_id, netfn, cmd, data[0] (select), data[1] (bypass netfn), data[2] (bypass cmd) if (tlen < 0) { completion_code = CC_INVALID_LENGTH; break; } netfn = req_data[1]; cmd = req_data[2]; 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 = CC_SUCCESS; memcpy(&res_data[0], rbuf, rlen); *res_len = rlen; } break; default: return completion_code; } return completion_code; }