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;
}