meta-facebook/meta-fbep/recipes-fbep/plat-libs/files/pal/pal.c (1,021 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include <dirent.h>
#include <openbmc/libgpio.h>
#include <openbmc/ipmi.h>
#include <openbmc/obmc-i2c.h>
#include <facebook/asic.h>
#include "pal.h"
#define DRIVER_READY "/tmp/driver_probed"
#define P12V_AUX_HWMON_DIR \
"/sys/bus/i2c/drivers/adm1275/18-0042/hwmon"
#define P12V_AUX_DIR \
P12V_AUX_HWMON_DIR"/hwmon%d/%s"
#define ADM1278_REBOOT "power_cycle"
#define P12V_1_HWMON_DIR \
"/sys/bus/i2c/drivers/ltc4282/16-0053/hwmon"
#define P12V_1_DIR \
P12V_1_HWMON_DIR"/hwmon%d/%s"
#define P12V_2_HWMON_DIR \
"/sys/bus/i2c/drivers/ltc4282/17-0040/hwmon"
#define P12V_2_DIR \
P12V_2_HWMON_DIR"/hwmon%d/%s"
#define LTC4282_AUX_HWMON_DIR \
"/sys/bus/i2c/drivers/ltc4282/18-0043/hwmon"
#define LTC4282_AUX_DIR \
LTC4282_AUX_HWMON_DIR"/hwmon%d/%s"
#define LTC4282_REBOOT "reboot"
#define DELAY_POWER_CYCLE 10
#define MAX_RETRY 10
#define BMC_IPMB_SLAVE_ADDR 0x16
#define OFFSET_DEV_GUID 0x1800
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define LAST_KEY "last_key"
const char pal_fru_list[] =
"all, mb, pdb, bsm, asic0, asic1, asic2, asic3, asic4, asic5, asic6, asic7";
const char pal_server_list[] = "mb";
char g_dev_guid[GUID_SIZE] = {0};
static int get_board_rev_id(uint8_t*);
static int key_set_server_type(int, void*);
enum key_event {
KEY_BEFORE_SET,
KEY_AFTER_INI,
};
struct pal_key_cfg {
char *name;
char *def_val;
int (*function)(int, void*);
} key_cfg[] = {
/* name, default value, function */
{"identify_sled", "off", NULL},
{"timestamp_sled", "0", NULL},
{KEY_MB_SNR_HEALTH, "1", NULL},
{KEY_MB_SEL_ERROR, "1", NULL},
{KEY_PDB_SNR_HEALTH, "1", NULL},
{KEY_ASIC0_SNR_HEALTH, "1", NULL},
{KEY_ASIC1_SNR_HEALTH, "1", NULL},
{KEY_ASIC2_SNR_HEALTH, "1", NULL},
{KEY_ASIC3_SNR_HEALTH, "1", NULL},
{KEY_ASIC4_SNR_HEALTH, "1", NULL},
{KEY_ASIC5_SNR_HEALTH, "1", NULL},
{KEY_ASIC6_SNR_HEALTH, "1", NULL},
{KEY_ASIC7_SNR_HEALTH, "1", NULL},
{"server_type", "4", key_set_server_type},
{"asic_mfr", MFR_NVIDIA, NULL},
{"ntp_server", "", NULL},
/* Add more Keys here */
{LAST_KEY, LAST_KEY, NULL} /* This is the last key of the list */
};
static int key_set_server_type(int event, void *arg)
{
int ret, fd;
off_t offset = 1030;
char *mode = (char*)arg;
uint8_t val;
if (event == KEY_AFTER_INI) // Do nothing
return 0;
// Update server type in EEPROM
fd = open(MB_EEPROM, O_RDWR);
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
if (ret < 0)
goto exit;
if (!strcmp(mode, "2")) {
val = SKU_2S;
} else if (!strcmp(mode, "4")) {
val = SKU_4SEX;
} else if (!strcmp(mode, "8")) {
val = SKU_4S;
} else {
syslog(LOG_WARNING, "Server socket mode %s is not supported", mode);
ret = -1;
goto exit;
}
if (write(fd, &val, 1) != 1)
ret = -1;
exit:
close(fd);
return ret;
}
int write_device(const char *device, int value)
{
FILE *fp;
char val[8] = {0};
fp = fopen(device, "w");
if (fp == NULL) {
#ifdef DEBUG
syslog(LOG_WARNING, "failed to write device %s", device);
#endif
return -1;
}
snprintf(val, 8, "%d", value);
fputs(val, fp);
fclose(fp);
return 0;
}
int read_device(const char *device, int *value)
{
FILE *fp;
int ret = 0;
fp = fopen(device, "r");
if (fp == NULL) {
#ifdef DEBUG
syslog(LOG_WARNING, "failed to read device %s", device);
#endif
return -1;
}
if (fscanf(fp, "%d", value) != 1) {
ret = -1;
}
fclose(fp);
return ret;
}
static int pal_key_index(char *key)
{
int i = 0;
while (strcmp(key_cfg[i].name, LAST_KEY)) {
// If Key is valid, return success
if (!strcmp(key, key_cfg[i].name))
return i;
i++;
}
#ifdef DEBUG
syslog(LOG_WARNING, "pal_key_index: invalid key - %s", key);
#endif
return -1;
}
int pal_get_key_value(char *key, char *value)
{
int index;
// Check is key is defined and valid
if ((index = pal_key_index(key)) < 0)
return -1;
return kv_get(key, value, NULL, KV_FPERSIST);
}
int pal_set_key_value(char *key, char *value)
{
int index, ret;
// Check is key is defined and valid
if ((index = pal_key_index(key)) < 0)
return -1;
if (key_cfg[index].function) {
ret = key_cfg[index].function(KEY_BEFORE_SET, value);
if (ret < 0)
return ret;
}
return kv_set(key, value, 0, KV_FPERSIST);
}
int pal_set_def_key_value()
{
int i;
char key[MAX_KEY_LEN] = {0};
for (i = 0; strcmp(key_cfg[i].name, LAST_KEY) != 0; i++) {
if (kv_set(key_cfg[i].name, key_cfg[i].def_val, 0, KV_FCREATE | KV_FPERSIST)) {
#ifdef DEBUG
syslog(LOG_WARNING, "pal_set_def_key_value: kv_set failed.");
#endif
}
if (key_cfg[i].function) {
key_cfg[i].function(KEY_AFTER_INI, key_cfg[i].name);
}
}
/* Actions to be taken on Power On Reset */
if (pal_is_bmc_por()) {
/* Clear all the SEL errors */
/* Write the value "1" which means FRU_STATUS_GOOD */
memset(key, 0, MAX_KEY_LEN);
strcpy(key, KEY_MB_SEL_ERROR);
pal_set_key_value(key, "1");
/* Clear all the sensor health files*/
/* Write the value "1" which means FRU_STATUS_GOOD */
memset(key, 0, MAX_KEY_LEN);
strcpy(key, KEY_MB_SNR_HEALTH);
pal_set_key_value(key, "1");
memset(key, 0, MAX_KEY_LEN);
strcpy(key, KEY_PDB_SNR_HEALTH);
pal_set_key_value(key, "1");
}
return 0;
}
int pal_channel_to_bus(int channel)
{
switch (channel) {
case 0:
return 13; // USB (LCD Debug Board)
case 1:
return 2; // MB#0
case 2:
return 3; // MB#1
case 3:
return 0; // MB#2
case 4:
return 1; // MB#3
}
// Debug purpose, map to real bus number
if (channel & 0x80) {
return (channel & 0x7f);
}
return channel;
}
int pal_get_fruid_path(uint8_t fru, char *path)
{
if (fru == FRU_MB)
sprintf(path, MB_BIN);
else if (fru == FRU_PDB)
sprintf(path, PDB_BIN);
else if (fru == FRU_BSM)
sprintf(path, BSM_BIN);
else
return -1;
return 0;
}
int pal_get_fruid_eeprom_path(uint8_t fru, char *path)
{
uint8_t rev_id;
if (fru == FRU_MB) {
sprintf(path, MB_EEPROM);
} else if (fru == FRU_PDB) {
sprintf(path, PDB_EEPROM);
} else if (fru == FRU_BSM) {
get_board_rev_id(&rev_id);
if (rev_id < 0x4)
sprintf(path, BSM_EEPROM, 13, 13);
else
sprintf(path, BSM_EEPROM, 4, 4);
} else {
return -1;
}
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_MB:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL |
FRU_CAPABILITY_SERVER | FRU_CAPABILITY_POWER_STATUS |
FRU_CAPABILITY_POWER_ON | FRU_CAPABILITY_POWER_OFF |
FRU_CAPABILITY_POWER_CYCLE;
break;
case FRU_PDB:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL;
break;
case FRU_BSM:
*caps = FRU_CAPABILITY_FRUID_ALL | FRU_CAPABILITY_SENSOR_ALL |
FRU_CAPABILITY_MANAGEMENT_CONTROLLER;
break;
case FRU_ASIC0:
case FRU_ASIC1:
case FRU_ASIC2:
case FRU_ASIC3:
case FRU_ASIC4:
case FRU_ASIC5:
case FRU_ASIC6:
case FRU_ASIC7:
*caps = FRU_CAPABILITY_SENSOR_ALL;
break;
default:
ret = -1;
break;
}
return ret;
}
int pal_get_fru_id(char *str, uint8_t *fru)
{
if (!strcmp(str, "all")) {
*fru = FRU_ALL;
} else if (!strcmp(str, "mb") || !strcmp(str, "vr") || !strcmp(str, "bmc") ||
!strcmp(str, "ocpdbg") || !strcmp(str, "asic")) {
*fru = FRU_MB;
} else if (!strcmp(str, "pdb")) {
*fru = FRU_PDB;
} else if (!strcmp(str, "bsm")) {
*fru = FRU_BSM;
} else if (!strcmp(str, "asic0")) {
*fru = FRU_ASIC0;
} else if (!strcmp(str, "asic1")) {
*fru = FRU_ASIC1;
} else if (!strcmp(str, "asic2")) {
*fru = FRU_ASIC2;
} else if (!strcmp(str, "asic3")) {
*fru = FRU_ASIC3;
} else if (!strcmp(str, "asic4")) {
*fru = FRU_ASIC4;
} else if (!strcmp(str, "asic5")) {
*fru = FRU_ASIC5;
} else if (!strcmp(str, "asic6")) {
*fru = FRU_ASIC6;
} else if (!strcmp(str, "asic7")) {
*fru = FRU_ASIC7;
} else {
syslog(LOG_WARNING, "%s: Wrong fru name %s", __func__, str);
return -1;
}
return 0;
}
int pal_get_fruid_name(uint8_t fru, char *name)
{
if (fru == FRU_MB)
sprintf(name, "Base Board");
else if (fru == FRU_PDB)
sprintf(name, "PDB");
else if (fru == FRU_BSM)
sprintf(name, "BSM");
else
return -1;
return 0;
}
int pal_is_fru_ready(uint8_t fru, uint8_t *status)
{
if (fru > FRU_ASIC7)
return -1;
*status = 1;
return 0;
}
int pal_get_fru_name(uint8_t fru, char *name)
{
switch (fru) {
case FRU_MB:
sprintf(name, "mb");
break;
case FRU_PDB:
sprintf(name, "pdb");
break;
case FRU_ASIC0:
sprintf(name, "asic0");
break;
case FRU_ASIC1:
sprintf(name, "asic1");
break;
case FRU_ASIC2:
sprintf(name, "asic2");
break;
case FRU_ASIC3:
sprintf(name, "asic3");
break;
case FRU_ASIC4:
sprintf(name, "asic4");
break;
case FRU_ASIC5:
sprintf(name, "asic5");
break;
case FRU_ASIC6:
sprintf(name, "asic6");
break;
case FRU_ASIC7:
sprintf(name, "asic7");
break;
case FRU_BSM:
sprintf(name, "bsm");
break;
default:
syslog(LOG_WARNING, "%s: Wrong fruid %d", __func__, fru);
return -1;
}
return 0;
}
int pal_get_bmc_ipmb_slave_addr(uint16_t *slave_addr, uint8_t bus_id)
{
if (bus_id == 13) {
// DBG Card used default slave addr
*slave_addr = 0x10;
} else {
*slave_addr = BMC_IPMB_SLAVE_ADDR;
}
return 0;
}
// GUID for System and Device
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(MB_EEPROM, F_OK) == -1) {
syslog(LOG_ERR, "pal_get_guid: unable to access the %s file: %s",
MB_EEPROM, strerror(errno));
return errno;
}
// Open the file
fd = open(MB_EEPROM, O_RDONLY);
if (fd == -1) {
syslog(LOG_ERR, "pal_get_guid: unable to open the %s file: %s",
MB_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",
MB_EEPROM, strerror(errno));
goto err_exit;
}
err_exit:
close(fd);
return errno;
}
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(MB_EEPROM, F_OK) == -1) {
syslog(LOG_ERR, "pal_set_guid: unable to access the %s file: %s",
MB_EEPROM, strerror(errno));
return errno;
}
// Open file
fd = open(MB_EEPROM, O_WRONLY);
if (fd == -1) {
syslog(LOG_ERR, "pal_set_guid: unable to open the %s file: %s",
MB_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",
MB_EEPROM, strerror(errno));
goto err_exit;
}
err_exit:
close(fd);
return errno;
}
int pal_set_dev_guid(uint8_t fru, char *str) {
pal_populate_guid(g_dev_guid, str);
return pal_set_guid(OFFSET_DEV_GUID, g_dev_guid);
}
int pal_get_dev_guid(uint8_t fru, char *guid) {
pal_get_guid(OFFSET_DEV_GUID, g_dev_guid);
memcpy(guid, g_dev_guid, GUID_SIZE);
return 0;
}
int pal_get_server_power(uint8_t fru, uint8_t *status)
{
gpio_desc_t *desc;
gpio_value_t value;
if (fru != FRU_MB)
return -1;
desc = gpio_open_by_shadow("SYS_PWR_READY");
if (!desc) {
#ifdef DEBUG
syslog(LOG_WARNING, "Open GPIO SYS_PWR_READY failed");
#endif
return -1;
}
if (gpio_get_value(desc, &value) < 0) {
syslog(LOG_WARNING, "Get GPIO SYS_PWR_READY failed");
value = GPIO_VALUE_INVALID;
}
gpio_close(desc);
*status = (value == GPIO_VALUE_HIGH)? SERVER_POWER_ON: SERVER_POWER_OFF;
return 0;
}
int pal_set_usb_path(uint8_t slot, uint8_t endpoint)
{
int ret = CC_SUCCESS;
char gpio_name[16] = {0};
gpio_desc_t *desc;
gpio_value_t value;
if (slot > SERVER_4 || endpoint > PCH) {
ret = CC_PARAM_OUT_OF_RANGE;
goto exit;
}
desc = gpio_open_by_shadow("SEL_USB_MUX");
if (!desc) {
ret = CC_UNSPECIFIED_ERROR;
goto exit;
}
value = endpoint == BMC? GPIO_VALUE_LOW: GPIO_VALUE_HIGH;
if (gpio_set_value(desc, value)) {
ret = CC_UNSPECIFIED_ERROR;
goto bail;
}
gpio_close(desc);
strcpy(gpio_name, endpoint == BMC? "USB2_SEL0_U43": "USB2_SEL0_U42");
desc = gpio_open_by_shadow(gpio_name);
if (!desc) {
ret = CC_UNSPECIFIED_ERROR;
goto exit;
}
value = slot%2 == 0? GPIO_VALUE_HIGH: GPIO_VALUE_LOW;
if (gpio_set_value(desc, value)) {
ret = CC_UNSPECIFIED_ERROR;
goto bail;
}
gpio_close(desc);
strcpy(gpio_name, endpoint == BMC? "USB2_SEL1_U43": "USB2_SEL1_U42");
desc = gpio_open_by_shadow(gpio_name);
if (!desc) {
ret = CC_UNSPECIFIED_ERROR;
goto exit;
}
value = slot/2 == 0? GPIO_VALUE_HIGH: GPIO_VALUE_LOW;
if (gpio_set_value(desc, value)) {
ret = CC_UNSPECIFIED_ERROR;
goto bail;
}
bail:
gpio_close(desc);
exit:
return ret;
}
static int server_power_on()
{
int ret = -1;
gpio_desc_t *gpio = gpio_open_by_shadow("BMC_IPMI_PWR_ON");
if (!gpio) {
return -1;
}
if (gpio_set_value(gpio, GPIO_VALUE_HIGH)) {
goto bail;
}
if (gpio_set_value(gpio, GPIO_VALUE_LOW)) {
goto bail;
}
sleep(1);
if (gpio_set_value(gpio, GPIO_VALUE_HIGH)) {
goto bail;
}
sleep(2);
ret = 0;
bail:
gpio_close(gpio);
return ret;
}
static int server_power_off()
{
int ret = -1;
gpio_desc_t *gpio = gpio_open_by_shadow("BMC_IPMI_PWR_ON");
if (!gpio) {
return -1;
}
if (gpio_set_value(gpio, GPIO_VALUE_HIGH)) {
goto bail;
}
if (gpio_set_value(gpio, GPIO_VALUE_LOW)) {
goto bail;
}
sleep(1);
if (gpio_set_value(gpio, GPIO_VALUE_HIGH)) {
goto bail;
}
ret = 0;
bail:
gpio_close(gpio);
return ret;
}
bool pal_is_server_off()
{
uint8_t status;
if (pal_get_server_power(FRU_MB, &status) < 0)
return false;
return status == SERVER_POWER_OFF? true: false;
}
bool is_device_ready()
{
if (access(DRIVER_READY, F_OK) == 0)
return !pal_is_server_off();
return false;
}
static bool cpld_power_permission()
{
bool ret = false;
gpio_value_t value;
gpio_desc_t *gpio = gpio_open_by_shadow("PWR_CTRL");
if (!gpio) {
return false;
}
if (gpio_get_value(gpio, &value) < 0) {
goto bail;
}
if (value == GPIO_VALUE_HIGH)
ret = true;
else
printf("Block power change while server is present\n");
bail:
gpio_close(gpio);
return ret;
}
// Power Off, Power On, or Power Cycle
int pal_set_server_power(uint8_t fru, uint8_t cmd)
{
uint8_t status;
if (!cpld_power_permission())
return -2; // make power-util not to retry
if (pal_get_server_power(fru, &status) < 0) {
return -1;
}
switch (cmd) {
case SERVER_POWER_ON:
return status == SERVER_POWER_ON? 1: server_power_on();
break;
case SERVER_POWER_OFF:
return status == SERVER_POWER_OFF? 1: server_power_off();
break;
case SERVER_POWER_CYCLE:
if (status == SERVER_POWER_ON) {
if (server_power_off())
return -1;
sleep(DELAY_POWER_CYCLE);
return server_power_on();
} else if (status == SERVER_POWER_OFF) {
return server_power_on();
}
break;
default:
return -1;
}
return 0;
}
void* chassis_control_handler(void *arg)
{
int cmd = (int)arg;
switch (cmd) {
case 0x00: // power off
if (pal_set_server_power(FRU_MB, SERVER_POWER_OFF) < 0)
syslog(LOG_CRIT, "SERVER_POWER_OFF failed");
else
syslog(LOG_CRIT, "SERVER_POWER_OFF successful");
break;
case 0x01: // power on
if (pal_set_server_power(FRU_MB, SERVER_POWER_ON) < 0)
syslog(LOG_CRIT, "SERVER_POWER_ON failed");
else
syslog(LOG_CRIT, "SERVER_POWER_ON successful");
break;
case 0x02: // power cycle
if (pal_set_server_power(FRU_MB, SERVER_POWER_CYCLE) < 0)
syslog(LOG_CRIT, "SERVER_POWER_CYCLE failed");
else
syslog(LOG_CRIT, "SERVER_POWER_CYCLE successful");
break;
case 0xAC: // sled-cycle with delay 4 secs
sleep(3);
pal_update_ts_sled();
sync();
sleep(1);
pal_force_sled_cycle();
break;
default:
syslog(LOG_CRIT, "Invalid command 0x%x for chassis control", cmd);
break;
}
pthread_exit(0);
}
int pal_chassis_control(uint8_t fru, uint8_t *req_data, uint8_t req_len)
{
int cmd;
pthread_t tid;
pthread_attr_t a;
if (fru == 2 && !cpld_power_permission()) {
// If came from BMC itself
return CC_NOT_SUPP_IN_CURR_STATE;
}
if (req_len != 1) {
return CC_INVALID_LENGTH;
}
if (pal_is_fw_update_ongoing(FRU_MB)) {
return CC_NOT_SUPP_IN_CURR_STATE;
}
cmd = req_data[0];
pthread_attr_init(&a);
pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
if (pthread_create(&tid, &a, chassis_control_handler, (void *)cmd) < 0) {
syslog(LOG_WARNING, "ipmid: chassis_control_handler pthread_create failed\n");
return CC_UNSPECIFIED_ERROR;
}
return CC_SUCCESS;
}
void pal_get_chassis_status(uint8_t fru, uint8_t *req_data, uint8_t *res_data, uint8_t *res_len)
{
int policy = POWER_CFG_ON; // Always On
unsigned char *data = res_data;
*data++ = ((pal_is_server_off())?0x00:0x01) | (policy << 5);
*data++ = 0x00; // Last Power Event
*data++ = 0x40; // Misc. Chassis Status
*data++ = 0x00; // Front Panel Button Disable
*res_len = data - res_data;
}
int pal_sled_cycle(void)
{
return cpld_power_permission()? pal_force_sled_cycle(): -1;
}
// TODO:
// Remove evt_compat if all of EVT PDB are replaced
int pal_force_sled_cycle(void)
{
int index, i;
struct dirent *dp;
DIR *dir;
char device[LARGEST_DEVICE_NAME] = {0};
const char *HSC_DIR[6] = {
P12V_1_HWMON_DIR, P12V_2_HWMON_DIR, P12V_AUX_HWMON_DIR,
P12V_1_DIR, P12V_2_DIR, P12V_AUX_DIR
};
for (i = 0; i < 3; i++) {
dir = opendir(HSC_DIR[i]);
if (dir == NULL) {
if (i == 2)
goto evt_compat;
else
goto err_exit;
}
while ((dp = readdir(dir)) != NULL) {
if (sscanf(dp->d_name, "hwmon%d", &index))
break;
}
if (dp == NULL) {
closedir(dir);
if (i == 2)
goto evt_compat;
else
goto err_exit;
}
closedir(dir);
// reboot HSC
if (i == 2)
snprintf(device, LARGEST_DEVICE_NAME, P12V_AUX_DIR, index, ADM1278_REBOOT);
else
snprintf(device, LARGEST_DEVICE_NAME, HSC_DIR[i+3], index, LTC4282_REBOOT);
if (write_device(device, 1) < 0) {
if (i == 2)
goto evt_compat;
else
goto err_exit;
}
}
return 0;
evt_compat:
dir = opendir(LTC4282_AUX_HWMON_DIR);
if (dir == NULL)
goto err_exit;
while ((dp = readdir(dir)) != NULL) {
if (sscanf(dp->d_name, "hwmon%d", &index))
break;
}
if (dp == NULL) {
closedir(dir);
goto err_exit;
}
closedir(dir);
snprintf(device, LARGEST_DEVICE_NAME, LTC4282_AUX_DIR, index, LTC4282_REBOOT);
if (write_device(device, 1) < 0)
goto err_exit;
err_exit:
syslog(LOG_CRIT, "SLED Cycle failed!\n");
return -1;
}
int pal_is_fru_prsnt(uint8_t fru, uint8_t *status)
{
switch (fru) {
case FRU_MB:
case FRU_PDB:
case FRU_BSM:
*status = 1;
break;
case FRU_ASIC0:
case FRU_ASIC1:
case FRU_ASIC2:
case FRU_ASIC3:
case FRU_ASIC4:
case FRU_ASIC5:
case FRU_ASIC6:
case FRU_ASIC7:
*status = is_asic_prsnt(fru-FRU_ASIC0)? 1: 0;
break;
default:
return -1;
}
return 0;
}
static void fan_state_led_ctrl(uint8_t snr_num, bool assert)
{
char blue_led[16] = {0};
char amber_led[16] = {0};
static uint8_t assert_flag[4] = {0};
gpio_desc_t *fan_ok;
gpio_desc_t *fan_fail;
uint8_t fan_id = snr_num >> 2;
snprintf(blue_led, sizeof(blue_led), "FAN%d_OK", (int)fan_id);
snprintf(amber_led, sizeof(amber_led), "FAN%d_FAIL", (int)fan_id);
fan_ok = gpio_open_by_shadow(blue_led);
fan_fail = gpio_open_by_shadow(amber_led);
if (!fan_ok || !fan_fail) {
syslog(LOG_WARNING, "FAN LED open failed");
goto exit;
}
if (!assert)
assert_flag[fan_id] &= ~(0x1 << (snr_num & ~(0xc)));
if (!assert_flag[fan_id]) {
// If all of assertion had been cleared or it is the first assertion
if (gpio_set_value(fan_fail, assert? GPIO_VALUE_HIGH: GPIO_VALUE_LOW) ||
gpio_set_value(fan_ok, assert? GPIO_VALUE_LOW: GPIO_VALUE_HIGH)) {
syslog(LOG_WARNING, "FAN LED control failed");
}
}
if (assert)
assert_flag[fan_id] |= (0x1 << (snr_num & ~(0xc)));
exit:
gpio_close(fan_ok);
gpio_close(fan_fail);
}
void pal_sensor_assert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh)
{
if (snr_num <= MB_FAN3_CURR)
fan_state_led_ctrl(snr_num, true);
}
void pal_sensor_deassert_handle(uint8_t fru, uint8_t snr_num, float val, uint8_t thresh)
{
if (snr_num <= MB_FAN3_CURR)
fan_state_led_ctrl(snr_num, false);
}
int pal_get_platform_id(uint8_t *id)
{
static bool cached = false;
static unsigned int cached_id = 0;
if (!cached) {
const char *shadows[] = {
"BOARD_ID0",
"BOARD_ID1",
"BOARD_ID2"
};
if (gpio_get_value_by_shadow_list(shadows, ARRAY_SIZE(shadows), &cached_id)) {
return -1;
}
cached = true;
}
*id = (uint8_t)cached_id;
return 0;
}
static int get_board_rev_id(uint8_t *id)
{
static bool cached = false;
static unsigned int cached_id = 0;
if (!cached) {
const char *shadows[] = {
"REV_ID0",
"REV_ID1",
"REV_ID2"
};
if (gpio_get_value_by_shadow_list(shadows, ARRAY_SIZE(shadows), &cached_id)) {
return -1;
}
cached = true;
}
*id = (uint8_t)cached_id;
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)
{
uint8_t platform_id = 0x00;
uint8_t board_rev_id = 0x00;
if (pal_get_platform_id(&platform_id) < 0) {
*res_len = 0x00;
return CC_UNSPECIFIED_ERROR;
}
if (get_board_rev_id(&board_rev_id) < 0) {
*res_len = 0x00;
return CC_UNSPECIFIED_ERROR;
}
res_data[0] = platform_id;
res_data[1] = board_rev_id;
*res_len = 0x02;
return CC_SUCCESS;
}
int pal_get_sysfw_ver(uint8_t fru, uint8_t *ver)
{
// No BIOS
return -1;
}
void pal_get_eth_intf_name(char* intf_name)
{
snprintf(intf_name, 8, "usb0");
}
void pal_dump_key_value(void)
{
int ret;
int i = 0;
char value[MAX_VALUE_LEN] = {0x0};
while (strcmp(key_cfg[i].name, LAST_KEY)) {
printf("%s:", key_cfg[i].name);
if ((ret = kv_get(key_cfg[i].name, value, NULL, KV_FPERSIST)) < 0) {
printf("\n");
} else {
printf("%s\n", value);
}
i++;
memset(value, 0, MAX_VALUE_LEN);
}
}
int pal_set_host_system_mode(uint8_t mode)
{
int ret;
if (mode == 0x00) {
ret = pal_set_key_value("server_type", "8"); // 4S
} else if (mode == 0x01) {
ret = pal_set_key_value("server_type", "4"); // 4SEX
} else if (mode == 0x02) {
ret = pal_set_key_value("server_type", "2"); // 2S
} else {
return CC_INVALID_PARAM;
}
return ret < 0? CC_UNSPECIFIED_ERROR: CC_SUCCESS;
}
int pal_set_id_led(uint8_t status)
{
gpio_desc_t *desc;
gpio_value_t value;
desc = gpio_open_by_shadow("SYSTEM_LOG_LED");
if (!desc) {
#ifdef DEBUG
syslog(LOG_WARNING, "Open GPIO SYSTEM_LOG_LED failed");
#endif
return -1;
}
value = status? GPIO_VALUE_HIGH: GPIO_VALUE_LOW;
if (gpio_set_value(desc, value)) {
syslog(LOG_ERR, "Set GPIO SYSTEM_LOG_LED failed\n");
return -1;
}
gpio_close(desc);
return 0;
}
int
pal_fw_update_finished(uint8_t fru, const char *comp, int status) {
int ret, ifd, retry = 3;
uint8_t buf[8];
char dev_i2c[16];
ret = status;
if (ret) {
return ret;
}
sync();
sleep(2);
printf("sending update intent to CPLD...\n");
fflush(stdout);
sprintf(dev_i2c, "/dev/i2c-%d", PFR_MAILBOX_BUS);
ifd = open(dev_i2c, O_RDWR);
if (ifd < 0) {
return -1;
}
buf[0] = 0x13; // BMC update intent
if (!strcmp(comp, "pfr_cpld")) {
buf[1] = UPDATE_CPLD_ACTIVE;
} else if (!strcmp(comp, "pfr_cpld_rc")) {
buf[1] = UPDATE_CPLD_RECOVERY;
} else {
close(ifd);
return -1;
}
do {
ret = i2c_rdwr_msg_transfer(ifd, PFR_MAILBOX_ADDR, buf, 2, NULL, 0);
if (ret) {
syslog(LOG_WARNING, "send update intent failed, cmd: %02x %02x", buf[0], buf[1]);
if (--retry > 0)
msleep(100);
}
} while (ret && retry > 0);
close(ifd);
return ret;
}
int
pal_is_pfr_active(void) {
int ifd, retry = 3;
uint8_t tbuf[8], rbuf[8];
char dev_i2c[16];
static bool cached = false;
static int pfr_active = PFR_NONE;
if (cached) {
return pfr_active;
}
sprintf(dev_i2c, "/dev/i2c-%d", PFR_MAILBOX_BUS);
ifd = open(dev_i2c, O_RDWR);
if (ifd < 0) {
return pfr_active;
}
tbuf[0] = 0x0A;
do {
if (!i2c_rdwr_msg_transfer(ifd, PFR_MAILBOX_ADDR, tbuf, 1, rbuf, 1)) {
pfr_active = (rbuf[0] & 0x20) ? PFR_ACTIVE : PFR_UNPROVISIONED;
break;
}
#ifdef DEBUG
syslog(LOG_WARNING, "i2c%u xfer failed, cmd: %02x", 4, tbuf[0]);
#endif
if (--retry > 0)
msleep(20);
} while (retry > 0);
close(ifd);
cached = true;
return pfr_active;
}
int
pal_get_pfr_address(uint8_t fru, uint8_t *bus, uint8_t *addr, bool *bridged) {
if (fru != FRU_MB) {
return -1;
}
*bus = PFR_MAILBOX_BUS;
*addr = PFR_MAILBOX_ADDR;
*bridged = false;
return 0;
}
int pal_slotid_to_fruid(int slotid)
{
// FRU ID remapping, we start from FRU_MB = 1
return slotid + 1;
}
int pal_devnum_to_fruid(int devnum)
{
// 1 = server, 2 = BMC ifself
return devnum == 0? 2: devnum;
}