meta-facebook/meta-fbal/recipes-fbal/gpiod/files/gpiod.c (863 lines of code) (raw):
/*
* sensord
*
* Copyright 2015-present Facebook. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/file.h>
#include <pthread.h>
#include <openbmc/libgpio.h>
#include <openbmc/pal.h>
#include <openbmc/nm.h>
#include <openbmc/kv.h>
#include <openbmc/obmc-i2c.h>
#include <openbmc/misc-utils.h>
#define POLL_TIMEOUT -1
#define POWER_ON_STR "on"
#define POWER_OFF_STR "off"
#define SERVER_POWER_CHECK(power_on_time) \
do { \
uint8_t status = 0; \
pal_get_server_power(FRU_MB, &status); \
if (status != SERVER_POWER_ON) { \
return; \
} \
if (g_power_on_sec < power_on_time) { \
return; \
} \
}while(0)
#define TOUCH(path) \
{\
int fd = creat(path, 0644);\
if (fd) close(fd);\
}
static uint8_t g_caterr_irq = 0;
static uint8_t g_msmi_irq = 0;
static bool g_mcerr_ierr_assert = false;
static int g_uart_switch_count = 0;
static long int g_reset_sec = 0;
static long int g_power_on_sec = 0;
static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t caterr_mutex = PTHREAD_MUTEX_INITIALIZER;
static gpio_value_t g_server_power_status = GPIO_VALUE_INVALID;
static bool g_cpu_pwrgd_trig = false;
// For monitoring GPIOs on IO expender
struct gpioexppoll_config {
char shadow[32];
char desc[64];
char pwr_st;
gpio_edge_t edge;
gpio_value_t last;
gpio_value_t curr;
void (*init_gpio)(char* shadow, char* desc, gpio_value_t value);
void (*handler)(char* shadow, char* desc, gpio_value_t value);
};
// For thermaltrip config
struct cpld_register_config {
char shadow[32];
uint8_t addr;
uint8_t offset;
};
enum {
STBY,
PS_ON,
PS_ON_3S,
};
void cpu_vr_hot_init(char* shadow, char* desc, gpio_value_t value) {
if(value == GPIO_VALUE_LOW ) {
syslog(LOG_CRIT, "FRU: %d %s: %s - %s\n", FRU_MB, value ? "DEASSERT": "ASSERT", desc, shadow);
}
return;
}
void cpu_skt_init(char* shadow, char* desc, gpio_value_t value) {
int cpu_id = strcmp(shadow, "FM_CPU0_SKTOCC_LVT3_PLD_N") == 0 ? 0 : 1;
char key[MAX_KEY_LEN] = {0};
char kvalue[MAX_VALUE_LEN] = {0};
gpio_value_t prev_value = GPIO_VALUE_INVALID;
snprintf(key, sizeof(key), "cpu%d_skt_status", cpu_id);
if (kv_get(key, kvalue, NULL, KV_FPERSIST) == 0) {
prev_value = atoi( kvalue);
}
snprintf(kvalue, sizeof(kvalue), "%d", value);
kv_set(key, kvalue, 0, KV_FPERSIST);
if(value != prev_value ) {
syslog(LOG_CRIT, "FRU: %d %s: %s - %s\n", FRU_MB, value ? "DEASSERT": "ASSERT", desc, shadow);
}
}
void ioex_gpios_event_handle(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d %s: %s - %s\n", FRU_MB, value ? "DEASSERT": "ASSERT", desc, shadow);
return;
}
static
void cpu0_pvqq_abc_handler(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d CPU0 PVDDQ ABC VR HOT Warning %s\n", FRU_MB,
value ? "Deassertion": "Assertion");
}
static
void cpu0_pvqq_def_handler(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d CPU0 PVDDQ DEF VR HOT Warning %s\n", FRU_MB,
value ? "Deassertion": "Assertion");
}
static
void cpu1_pvqq_abc_handler(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d CPU1 PVDDQ ABC VR HOT Warning %s\n", FRU_MB,
value ? "Deassertion": "Assertion");
}
static
void cpu1_pvqq_def_handler(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d CPU1 PVDDQ DEF VR HOT Warning %s\n", FRU_MB,
value ? "Deassertion": "Assertion");
}
static
void cpu0_pvccin_vr_hot_handler(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d CPU0 PVCCIN VR HOT Warning %s\n", FRU_MB,
value ? "Deassertion": "Assertion");
}
static
void cpu1_pvccin_vr_hot_handler(char* shadow, char* desc, gpio_value_t value) {
syslog(LOG_CRIT, "FRU: %d CPU1 PVCCIN VR HOT Warning %s\n", FRU_MB,
value ? "Deassertion": "Assertion");
}
static
void cpu0_pvccin_pwr_in_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
syslog(LOG_CRIT, "FRU: %d CPU0 PVCCIN POWER FAULT %s\n", FRU_MB,
curr ? "Deassertion": "Assertion");
}
static
void cpu1_pvccin_pwr_in_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
syslog(LOG_CRIT, "FRU: %d CPU1 PVCCIN POWER FAULT %s\n", FRU_MB,
curr ? "Deassertion": "Assertion");
}
static
void sml1_pmbus_alert_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
syslog(LOG_CRIT, "FRU: %d HSC OC Warning %s\n", FRU_MB,
curr ? "Deassertion": "Assertion");
}
static
void oc_detect_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
syslog(LOG_CRIT, "FRU: %d HSC Surge Current Warning %s\n", FRU_MB,
curr ? "Deassertion": "Assertion");
}
static
void uv_detect_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
syslog(LOG_CRIT, "FRU: %d HSC Under Voltage Warning %s\n", FRU_MB,
curr ? "Deassertion": "Assertion");
}
static
void hsc_timer_exp_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
syslog(LOG_CRIT, "FRU: %d HSC OCP Fault Warning %s\n", FRU_MB,
curr ? "Deassertion": "Assertion");
}
//PCA9539 Address 0XEE
struct gpioexppoll_config ioex0_gpios[] = {
{"IRQ_PVCCIN_CPU0_VRHOT_LVC3_N", "ExIO_0", PS_ON, GPIO_EDGE_BOTH, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_vr_hot_init, cpu0_pvccin_vr_hot_handler},
{"IRQ_PVCCIN_CPU1_VRHOT_LVC3_N", "ExIO_1", PS_ON, GPIO_EDGE_BOTH, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_vr_hot_init, cpu1_pvccin_vr_hot_handler},
{"IRQ_PVDDQ_ABC_CPU0_VRHOT_LVC3_N", "ExIO_2", PS_ON, GPIO_EDGE_BOTH, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_vr_hot_init, cpu0_pvqq_abc_handler},
{"IRQ_PVDDQ_DEF_CPU0_VRHOT_LVC3_N", "ExIO_3", PS_ON, GPIO_EDGE_BOTH, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_vr_hot_init, cpu0_pvqq_def_handler},
{"IRQ_PVDDQ_ABC_CPU1_VRHOT_LVC3_N", "ExIO_4", PS_ON, GPIO_EDGE_BOTH, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_vr_hot_init, cpu1_pvqq_abc_handler},
{"IRQ_PVDDQ_DEF_CPU1_VRHOT_LVC3_N", "ExIO_5", PS_ON, GPIO_EDGE_BOTH, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_vr_hot_init, cpu1_pvqq_def_handler},
{"FM_CPU0_SKTOCC_LVT3_PLD_N", "ExIO_6", STBY, GPIO_EDGE_RISING, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_skt_init, ioex_gpios_event_handle},
{"FM_CPU1_SKTOCC_LVT3_PLD_N", "ExIO_7", STBY, GPIO_EDGE_RISING, GPIO_VALUE_INVALID, GPIO_VALUE_INVALID, cpu_skt_init, ioex_gpios_event_handle},
};
static gpio_value_t gpio_get(const char *shadow)
{
gpio_value_t value = GPIO_VALUE_INVALID;
gpio_desc_t *desc = gpio_open_by_shadow(shadow);
if (!desc) {
syslog(LOG_CRIT, "FRU: %d Open failed for GPIO: %s\n", FRU_MB, shadow);
return GPIO_VALUE_INVALID;
}
if (gpio_get_value(desc, &value)) {
syslog(LOG_CRIT, "FRU: %d Get failed for GPIO: %s\n", FRU_MB, shadow);
value = GPIO_VALUE_INVALID;
}
gpio_close(desc);
return value;
}
/*
static int gpio_set(const char *shadow, gpio_value_t val)
{
gpio_desc_t *desc = gpio_open_by_shadow(shadow);
if (!desc) {
syslog(LOG_CRIT, "Open failed for GPIO: %s\n", shadow);
return -1;
}
if (gpio_set_value(desc, val)) {
syslog(LOG_CRIT, "Set failed for GPIO: %s\n", shadow);
gpio_close(desc);
return -1;
}
gpio_close(desc);
return 0;
}
*/
static void* ioex0_monitor()
{
int i;
uint8_t status;
uint8_t assert = false;
static uint8_t init_flag[8];
gpio_value_t curr;
while (1) {
for (i = 0; i < 8; i++) {
if(ioex0_gpios[i].pwr_st != STBY) {
if (pal_get_server_power(FRU_MB, &status) < 0 || status == SERVER_POWER_OFF) {
continue;
}
}
curr = gpio_get(ioex0_gpios[i].shadow);
if(curr == ioex0_gpios[i].last) {
continue;
}
if(init_flag[i] != true) {
if(ioex0_gpios[i].init_gpio != NULL) {
ioex0_gpios[i].init_gpio(ioex0_gpios[i].shadow, ioex0_gpios[i].desc, curr);
}
ioex0_gpios[i].last = curr;
init_flag[i] = true;
#ifdef DEBUG
syslog(LOG_DEBUG, "gpio %s initial value=%x\n", ioex0_gpios[i].shadow, curr);
#endif
continue;
}
#ifdef DEBUG
syslog(LOG_DEBUG, "edge gpio %s value=%x\n", ioex0_gpios[i].shadow, curr);
#endif
switch (ioex0_gpios[i].edge) {
case GPIO_EDGE_FALLING:
if(curr == GPIO_VALUE_LOW) {
assert = true;
} else {
assert = false;
}
break;
case GPIO_EDGE_RISING:
if(curr == GPIO_VALUE_HIGH) {
assert = true;
} else {
assert = false;
}
break;
case GPIO_EDGE_BOTH:
assert = true;
break;
default:
assert = false;
break;
}
if((assert == true) && (ioex0_gpios[i].handler != NULL)) {
ioex0_gpios[i].handler(ioex0_gpios[i].shadow, ioex0_gpios[i].desc, curr);
}
ioex0_gpios[i].last = curr;
}
sleep(1);
}
return NULL;
}
static void
log_gpio_change(gpiopoll_pin_t *desc, gpio_value_t value, useconds_t log_delay) {
const struct gpiopoll_config *cfg = gpio_poll_get_config(desc);
assert(cfg);
syslog(LOG_CRIT, "FRU: %d %s: %s - %s\n", FRU_MB, value ? "DEASSERT": "ASSERT",
cfg->description, cfg->shadow);
}
static inline long int
reset_timer(long int *val) {
pthread_mutex_lock(&timer_mutex);
*val = 0;
pthread_mutex_unlock(&timer_mutex);
return *val;
}
static inline long int
increase_timer(long int *val) {
pthread_mutex_lock(&timer_mutex);
(*val)++;
pthread_mutex_unlock(&timer_mutex);
return *val;
}
static inline long int
decrease_timer(long int *val) {
pthread_mutex_lock(&timer_mutex);
(*val)--;
pthread_mutex_unlock(&timer_mutex);
return *val;
}
//PCH Thermtrip Handler
static void pch_thermtrip_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
uint8_t status = 0;
gpio_value_t value;
value = gpio_get("RST_PLTRST_BMC_N");
if(value < 0 || value == GPIO_VALUE_LOW) {
return;
}
pal_get_server_power(FRU_MB, &status);
if (status != SERVER_POWER_ON)
return;
// Filter false positives during reset.
if (g_reset_sec < 10)
return;
log_gpio_change(desc, curr, 0);
}
//FIVR Fault Handler
static void fivr_fault_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
uint8_t status = 0;
gpio_value_t value;
value = gpio_get("RST_PLTRST_BMC_N");
if(value < 0 || value == GPIO_VALUE_LOW) {
return;
}
pal_get_server_power(FRU_MB, &status);
if (status != SERVER_POWER_ON)
return;
log_gpio_change(desc, curr, 0);
}
//PROCHOT Handler
static void prochot_reason(char *reason)
{
if (gpio_get("IRQ_UV_DETECT_N") == GPIO_VALUE_LOW)
strcpy(reason, "UV");
if (gpio_get("IRQ_OC_DETECT_N") == GPIO_VALUE_LOW)
strcpy(reason, "OC");
if (gpio_get("FM_HSC_TIMER_EXP_N") == GPIO_VALUE_LOW)
strcpy(reason, "timer exp");
if (gpio_get("IRQ_SML1_PMBUS_BMC_ALERT_N") == GPIO_VALUE_LOW)
strcpy(reason, "PMBus alert");
}
static void cpu_prochot_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
char cmd[128] = {0};
const struct gpiopoll_config *cfg = gpio_poll_get_config(desc);
assert(cfg);
SERVER_POWER_CHECK(3);
//LCD debug card critical SEL support
strcat(cmd, "CPU FPH");
if (curr) {
strcat(cmd, " DEASSERT");
syslog(LOG_CRIT, "FRU: %d DEASSERT: %s - %s\n", FRU_MB, cfg->description, cfg->shadow);
} else {
char reason[32] = "";
strcat(cmd, " by ");
prochot_reason(reason);
strcat(cmd, reason);
syslog(LOG_CRIT, "FRU: %d ASSERT: %s - %s (reason: %s)\n",
FRU_MB, cfg->description, cfg->shadow, reason);
strcat(cmd, " ASSERT");
}
pal_add_cri_sel(cmd);
}
static struct cpld_register_config cpld_registers[] = {
{"FM_CPU0_THERMTRIP_LVT3_PLD_N", 0x1e, 0x20}, // GPIOA1
{"FM_CPU1_THERMTRIP_LVT3_PLD_N", 0x1e, 0x21}, // GPIOD0
{"FM_MEM_THERM_EVENT_CPU0_LVT3_N", 0x1e, 0x22}, // GPIOB0
{"FM_MEM_THERM_EVENT_CPU1_LVT3_N", 0x1e, 0x23}, // GPIOB1
{"FM_CPU_CATERR_LVT3_N", 0x16, 0x10}, // GPIOZ0
{"FM_CPU_MSMI_LVT3_N", 0x16, 0x11}, // GPIOZ2
{"FM_CPU_ERR0_LVT3_N", 0x16, 0x12}, // GPIOF0
{"FM_CPU_ERR1_LVT3_N", 0x16, 0x13}, // GPIOF1
{"FM_CPU_ERR2_LVT3_N", 0x16, 0x14}, // GPIOF2
};
static void get_cpld_register_data (const char *shadow, uint8_t *addr, uint8_t *offset) {
int i, config_size = sizeof(cpld_registers) / sizeof(cpld_registers[0]);
for (i = 0; i < config_size; i++) {
if (strcmp(shadow, cpld_registers[i].shadow) == 0) {
*addr = cpld_registers[i].addr;
*offset = cpld_registers[i].offset;
return;
}
}
}
static int cpld_register_check(const char *shadow)
{
int fd = 0, retCode = -1;
uint8_t bus = 4;
uint8_t addr = 0x00;
uint8_t offset = 0x00;
uint8_t tlen, rlen;
uint8_t tbuf[16] = {0};
uint8_t rbuf[16] = {0};
get_cpld_register_data(shadow, &addr, &offset);
if (addr == 0 || offset == 0) {
syslog(LOG_WARNING, "There is no cpld register can check with %s\n", shadow);
return retCode;
}
fd = i2c_cdev_slave_open(bus, addr >> 1, I2C_SLAVE_FORCE_CLAIM);
if (fd < 0) {
syslog(LOG_WARNING, "%s() Failed to open %d", __func__, bus);
return retCode;
}
tbuf[0] = (uint8_t)offset;
tlen = 1;
rlen = 1;
retCode = i2c_rdwr_msg_transfer(fd, addr, tbuf, tlen, rbuf, rlen);
if (retCode == -1) {
syslog(LOG_WARNING, "%s bus=%x slavaddr=%x offset=%x\n", __func__, bus, addr >> 1, offset);
} else {
retCode = (int)rbuf[0];
}
i2c_cdev_slave_close(fd);
return retCode;
}
static void thermtrip_add_cri_sel(const char *shadow_name, gpio_value_t curr)
{
char cmd[128], log_name[16], log_descript[16] = "ASSERT\0";
if (strcmp(shadow_name, "FM_CPU0_THERMTRIP_LVT3_PLD_N") == 0)
sprintf(log_name, "CPU0");
else if (strcmp(shadow_name, "FM_CPU1_THERMTRIP_LVT3_PLD_N") == 0)
sprintf(log_name, "CPU1");
if (curr == GPIO_VALUE_HIGH)
sprintf(log_descript, "DEASSERT");
sprintf(cmd, "FRU: %d %s thermtrip %s", FRU_MB, log_name, log_descript);
pal_add_cri_sel(cmd);
}
static void cpu_thermtrip_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
const struct gpiopoll_config *cfg = gpio_poll_get_config(desc);
if (g_server_power_status != GPIO_VALUE_HIGH)
return;
if (cpld_register_check(cfg->shadow) <= 0)
return;
thermtrip_add_cri_sel(cfg->shadow, curr);
log_gpio_change(desc, curr, 0);
}
static void cpu_event_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
int cpld_register_count = 0;
const struct gpiopoll_config *cfg = gpio_poll_get_config(desc);
if (g_server_power_status != GPIO_VALUE_HIGH)
return;
cpld_register_count = cpld_register_check(cfg->shadow);
if (cpld_register_count == 0)
return;
assert(cfg);
syslog(LOG_CRIT, "FRU: %d %s: %s - %s, %d time%s\n",
FRU_MB, curr ? "DEASSERT": "ASSERT", cfg->description, cfg->shadow,
cpld_register_count, cpld_register_count > 1 ? "s": "");
}
static void mem_thermtrip_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
if (g_server_power_status != GPIO_VALUE_HIGH)
return;
log_gpio_change(desc, curr, 0);
}
// Generic Event Handler for GPIO changes
static void gpio_event_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
log_gpio_change(desc, curr, 0);
}
// Generic Event Handler for GPIO changes, but only logs event when MB is ON
static void gpio_event_pson_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
SERVER_POWER_CHECK(0);
log_gpio_change(desc, curr, 0);
}
// Generic Event Handler for GPIO changes, but only logs event when MB is ON 3S
static void gpio_event_pson_3s_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr)
{
SERVER_POWER_CHECK(3);
log_gpio_change(desc, curr, 0);
}
//Usb Debug Card Event Handler
static void
usb_dbg_card_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
log_gpio_change(desc, curr, 0);
}
//Reset Button Event Handler
static void
pwr_reset_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
log_gpio_change(desc, curr, 0);
}
//Power Button Event Handler
static void
pwr_button_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
pal_set_restart_cause(FRU_MB, RESTART_CAUSE_PWR_ON_PUSH_BUTTON);
log_gpio_change(desc, curr, 0);
}
static int
dimm_mux_recover_needed(int fd, uint8_t addr) {
int ret = 0;
uint8_t spd;
uint8_t tbuf[8] = {0};
uint8_t rbuf[8] = {0};
// check if DIMM_MUX is abnormal
do {
// DIMM_MUX
if ((ret = retry_cond(!i2c_rdwr_msg_transfer(fd, addr, tbuf, 0, rbuf, 1), 2, 10))) {
syslog(LOG_WARNING, "FRU: %d Failed to read DIMM_MUX status", FRU_MB);
break;
}
if (rbuf[0] & 0xF) {
syslog(LOG_WARNING, "FRU: %d Unexpected DIMM_MUX status %02x", FRU_MB, rbuf[0]);
ret = 1;
break;
}
// SPD
for (spd = 0xa0; spd <= 0xa8; spd += 2) {
if (!retry_cond(!i2c_rdwr_msg_transfer(fd, spd, tbuf, 0, rbuf, 1), 1, 10)) {
syslog(LOG_WARNING, "FRU: %d Found unexpected SPD %02x", FRU_MB, spd);
ret = 1;
break;
}
}
} while (0);
return ret;
}
static int
dimm_mux_check(void) {
int fd;
int ret = 0, rc_need = 0, retry = 1;
uint8_t bus = 4, addr = 0xe6;
uint8_t tbuf[8] = {0};
uint8_t rbuf[8] = {0};
fd = i2c_cdev_slave_open(bus, addr >> 1, I2C_SLAVE_FORCE_CLAIM);
if (fd < 0) {
syslog(LOG_WARNING, "%s() Failed to open i2c-%d", __func__, bus);
return -1;
}
do {
if (!(rc_need = dimm_mux_recover_needed(fd, addr))) {
break;
}
tbuf[0] = 0;
if ((ret = retry_cond(!i2c_rdwr_msg_transfer(fd, addr, tbuf, 1, rbuf, 0), 2, 10))) {
syslog(LOG_WARNING, "FRU: %d Failed to write DIMM_MUX", FRU_MB);
break;
}
if (!(rc_need = dimm_mux_recover_needed(fd, addr))) {
syslog(LOG_CRIT, "FRU: %d Cleared DIMM_MUX successfully", FRU_MB);
pal_set_server_power(FRU_MB, SERVER_POWER_RESET);
break;
}
} while (--retry >= 0);
if (ret && (rc_need > 0)) {
syslog(LOG_CRIT, "FRU: %d Failed to clear DIMM_MUX", FRU_MB);
}
i2c_cdev_slave_close(fd);
return ret;
}
//CPU Power Ok Event Handler
static void
cpu_pwr_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
g_server_power_status = curr;
g_cpu_pwrgd_trig = true;
log_gpio_change(desc, curr, 0);
reset_timer(&g_power_on_sec);
// workaround for unexpected DIMM_MUX status
if (g_server_power_status == GPIO_VALUE_HIGH) {
dimm_mux_check();
}
}
static void
init_cpu_pwr(gpiopoll_pin_t *desc, gpio_value_t value) {
g_server_power_status = value;
// workaround for unexpected DIMM_MUX status
if (g_server_power_status == GPIO_VALUE_HIGH) {
dimm_mux_check();
}
}
//IERR and MCERR Event Handler
static void
init_caterr(gpiopoll_pin_t *desc, gpio_value_t value) {
uint8_t status = 0;
pal_get_server_power(FRU_MB, &status);
if (status && value == GPIO_VALUE_LOW) {
g_caterr_irq++;
}
}
static void
init_msmi(gpiopoll_pin_t *desc, gpio_value_t value) {
uint8_t status = 0;
pal_get_server_power(FRU_MB, &status);
if (status && value == GPIO_VALUE_LOW) {
g_msmi_irq++;
}
}
static void
err_caterr_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
const struct gpiopoll_config *cfg = gpio_poll_get_config(desc);
SERVER_POWER_CHECK(1);
if (cpld_register_check(cfg->shadow) == 0)
return;
pthread_mutex_lock(&caterr_mutex);
g_caterr_irq++;
pthread_mutex_unlock(&caterr_mutex);
}
static void
err_msmi_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
const struct gpiopoll_config *cfg = gpio_poll_get_config(desc);
SERVER_POWER_CHECK(1);
if (cpld_register_check(cfg->shadow) <= 0)
return;
pthread_mutex_lock(&caterr_mutex);
g_msmi_irq++;
pthread_mutex_unlock(&caterr_mutex);
}
static int
ierr_mcerr_event_log(bool is_caterr, const char *err_type) {
char temp_log[128] = {0};
char temp_syslog[128] = {0};
char cpu_str[32] = "";
int num=0;
int ret=0;
ret = cmd_peci_get_cpu_err_num(&num, is_caterr);
if (ret != 0) {
syslog(LOG_ERR, "Can't Read MCA Log\n");
}
if (num == 2)
strcpy(cpu_str, "0/1");
else if (num != -1)
sprintf(cpu_str, "%d", num);
sprintf(temp_syslog, "ASSERT: CPU%s %s\n", cpu_str, err_type);
sprintf(temp_log, "CPU%s %s", cpu_str, err_type);
syslog(LOG_CRIT, "FRU: %d %s",FRU_MB, temp_syslog);
pal_add_cri_sel(temp_log);
return ret;
}
static void *
ierr_mcerr_event_handler() {
uint8_t caterr_cnt = 0;
uint8_t msmi_cnt = 0;
gpio_value_t value;
gpio_desc_t *caterr = gpio_open_by_shadow("FM_CPU_CATERR_LVT3_N");
gpio_desc_t *msmi = gpio_open_by_shadow("FM_CPU_MSMI_LVT3_N");
if (!caterr) {
gpio_close(caterr);
return NULL;
}
if (!msmi) {
gpio_close(msmi);
return NULL;
}
while (1) {
if (g_caterr_irq > 0) {
caterr_cnt++;
if (caterr_cnt == 2) {
if (g_caterr_irq == 1) {
g_mcerr_ierr_assert = true;
if (gpio_get_value(caterr, &value)) {
syslog(LOG_CRIT, "FRU: %d Getting CATERR GPIO failed", FRU_MB);
break;
}
if (value == GPIO_VALUE_LOW) {
ierr_mcerr_event_log(true, "IERR/CATERR");
} else {
ierr_mcerr_event_log(true, "MCERR/CATERR");
}
pthread_mutex_lock(&caterr_mutex);
g_caterr_irq--;
pthread_mutex_unlock(&caterr_mutex);
caterr_cnt = 0;
if (system("/usr/local/bin/autodump.sh &")) {
syslog(LOG_WARNING, "Failed to start crashdump\n");
}
} else if (g_caterr_irq > 1) {
while (g_caterr_irq > 1) {
ierr_mcerr_event_log(true, "MCERR/CATERR");
pthread_mutex_lock(&caterr_mutex);
g_caterr_irq--;
pthread_mutex_unlock(&caterr_mutex);
}
caterr_cnt = 1;
}
}
}
if (g_msmi_irq > 0) {
msmi_cnt++;
if (msmi_cnt == 2) {
if (g_msmi_irq == 1) {
g_mcerr_ierr_assert = true;
if (gpio_get_value(msmi, &value)) {
syslog(LOG_CRIT, "FRU: %d Getting MSMI GPIO failed", FRU_MB);
break;
}
if (value == GPIO_VALUE_LOW) {
ierr_mcerr_event_log(false, "IERR/MSMI");
} else {
ierr_mcerr_event_log(false, "MCERR/MSMI");
}
pthread_mutex_lock(&caterr_mutex);
g_msmi_irq--;
pthread_mutex_unlock(&caterr_mutex);
msmi_cnt = 0;
if (system("/usr/local/bin/autodump.sh &")) {
syslog(LOG_WARNING, "Failed to start crashdump\n");
}
} else if (g_msmi_irq > 1) {
while (g_msmi_irq > 1) {
ierr_mcerr_event_log(false, "MCERR/MSMI");
pthread_mutex_lock(&caterr_mutex);
g_msmi_irq--;
pthread_mutex_unlock(&caterr_mutex);
}
msmi_cnt = 1;
}
}
}
usleep(25000); //25ms
}
gpio_close(caterr);
gpio_close(msmi);
return NULL;
}
//Uart Select on DEBUG Card Event Handler
static void
uart_select_handle(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
g_uart_switch_count = 2;
log_gpio_change(desc, curr, 0);
pal_uart_select_led_set();
}
// Event Handler for GPIOF6 platform reset changes
static void platform_reset_handle(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
struct timespec ts;
char value[MAX_VALUE_LEN];
// Use GPIOF6 to filter some gpio logging
reset_timer(&g_reset_sec);
TOUCH("/tmp/rst_touch");
clock_gettime(CLOCK_MONOTONIC, &ts);
sprintf(value, "%ld", ts.tv_sec);
if( curr == GPIO_VALUE_HIGH )
kv_set("snr_pwron_flag", value, 0, 0);
else
kv_set("snr_pwron_flag", 0, 0, 0);
log_gpio_change(desc, curr, 0);
}
static void
upi_detect_handler(gpiopoll_pin_t *desc, gpio_value_t last, gpio_value_t curr) {
}
static void
upi_init_handler(gpiopoll_pin_t *desc, gpio_value_t value) {
int ret;
uint8_t mode;
ret = pal_get_host_system_mode(&mode);
if ( ret != 0 )
syslog(LOG_WARNING, "%s get mode fail\n", __func__);
//2S can't plug in UPI board, 4S can't lose UPI borad
if ( mode == MB_2S_MODE && value == GPIO_VALUE_LOW)
log_gpio_change(desc, value, 0);
else if ( (mode == MB_4S_EP_MODE || mode == MB_4S_EX_MODE) && value == GPIO_VALUE_HIGH )
log_gpio_change(desc, value, 0);
}
static void
platform_reset_init(gpiopoll_pin_t *desc, gpio_value_t value) {
struct timespec ts;
char data[MAX_VALUE_LEN];
clock_gettime(CLOCK_MONOTONIC, &ts);
sprintf(data, "%ld", ts.tv_sec);
if( value == GPIO_VALUE_HIGH )
kv_set("snr_pwron_flag", data, 0, 0);
else
kv_set("snr_pwron_flag", 0, 0, 0);
return;
}
// Thread for gpio timer
static void
*gpio_timer() {
uint8_t status = 0;
uint8_t fru = FRU_MB;
long int pot;
char str[MAX_VALUE_LEN] = {0};
int tread_time = 0;
while (1) {
sleep(1);
pal_get_server_power(fru, &status);
if (status == SERVER_POWER_ON) {
increase_timer(&g_reset_sec);
pot = increase_timer(&g_power_on_sec);
} else {
pot = decrease_timer(&g_power_on_sec);
}
// Wait power-on.sh finished, then update pwr_server_last_state
if (tread_time < 20) {
tread_time++;
} else {
if (pal_get_last_pwr_state(fru, str) < 0)
str[0] = '\0';
if (status == SERVER_POWER_ON) {
if (strncmp(str, POWER_ON_STR, strlen(POWER_ON_STR)) != 0) {
pal_set_last_pwr_state(fru, POWER_ON_STR);
syslog(LOG_INFO, "last pwr state updated to on\n");
}
} else {
// wait until PowerOnTime < -2 to make sure it's not AC lost
// Handle corner case during sled-cycle due to BMC residual electricity (1.2sec)
if (pot < -2 && strncmp(str, POWER_OFF_STR, strlen(POWER_OFF_STR)) != 0) {
pal_set_last_pwr_state(fru, POWER_OFF_STR);
syslog(LOG_INFO, "last pwr state updated to off\n");
}
}
}
//Show Uart Debug Select Number 2sec
if (g_uart_switch_count > 0) {
if (--g_uart_switch_count == 0)
pal_postcode_select(POSTCODE_BY_HOST);
}
}
return NULL;
}
// Thread for gpio timer
static void
*power_fail_monitor() {
static uint8_t init_cache=0;
static uint8_t retry=1;
while (1) {
if ( init_cache == 0 ) {
if ( g_power_on_sec > 0 ) {
g_cpu_pwrgd_trig = true;
init_cache = 1;
}
}
if ( g_cpu_pwrgd_trig == true ) {
sleep(2);
while ( pal_check_cpld_power_fail()) {
if( retry == 0) {
syslog(LOG_CRIT, "Check CPLD Power Fail Falure\n");
break;
}
retry--;
sleep(1);
}
g_cpu_pwrgd_trig = false;
retry=1;
}
sleep(2);
}
return NULL;
}
// GPIO table to be monitored
static struct gpiopoll_config g_gpios[] = {
// shadow, description, edge, handler, oneshot
{"FP_BMC_RST_BTN_N", "GPIOE0", GPIO_EDGE_BOTH, pwr_reset_handler, NULL},
{"FM_BMC_PWR_BTN_R_N", "GPIOE2", GPIO_EDGE_BOTH, pwr_button_handler, NULL},
{"FM_UARTSW_LSB_N", "GPIOL0", GPIO_EDGE_BOTH, uart_select_handle, NULL},
{"FM_UARTSW_MSB_N", "GPIOL1", GPIO_EDGE_BOTH, uart_select_handle, NULL},
{"FM_POST_CARD_PRES_BMC_N", "GPIOQ6", GPIO_EDGE_BOTH, usb_dbg_card_handler, NULL},
{"FM_PCH_BMC_THERMTRIP_N", "GPIOG2", GPIO_EDGE_BOTH, pch_thermtrip_handler, NULL},
{"RST_PLTRST_BMC_N", "GPIOF6", GPIO_EDGE_BOTH, platform_reset_handle, platform_reset_init},
{"IRQ_UV_DETECT_N", "GPIOM0", GPIO_EDGE_BOTH, uv_detect_handler, NULL},
{"IRQ_OC_DETECT_N", "GPIOM1", GPIO_EDGE_BOTH, oc_detect_handler, NULL},
{"IRQ_HSC_FAULT_N", "GPIOL2", GPIO_EDGE_BOTH, gpio_event_handler, NULL},
{"IRQ_SML1_PMBUS_BMC_ALERT_N", "GPIOAA1", GPIO_EDGE_BOTH, sml1_pmbus_alert_handler, NULL},
{"FM_CPU_CATERR_LVT3_N", "GPIOZ0", GPIO_EDGE_FALLING, err_caterr_handler, init_caterr},
{"FM_CPU_MSMI_LVT3_N", "GPIOZ2", GPIO_EDGE_FALLING, err_msmi_handler, init_msmi},
{"FM_CPU0_THERMTRIP_LVT3_PLD_N", "GPIOA1", GPIO_EDGE_FALLING, cpu_thermtrip_handler, NULL},
{"FM_CPU1_THERMTRIP_LVT3_PLD_N", "GPIOD0", GPIO_EDGE_FALLING, cpu_thermtrip_handler, NULL},
{"FM_CPU0_MEMHOT_OUT_N", "GPIOU5", GPIO_EDGE_BOTH, gpio_event_pson_3s_handler, NULL},
{"FM_CPU1_MEMHOT_OUT_N", "GPIOL3", GPIO_EDGE_BOTH, gpio_event_pson_3s_handler, NULL},
{"FM_CPU0_FIVR_FAULT_LVT3_PLD", "GPIOB2", GPIO_EDGE_BOTH, fivr_fault_handler, NULL},
{"FM_CPU1_FIVR_FAULT_LVT3_PLD", "GPIOB3", GPIO_EDGE_BOTH, fivr_fault_handler, NULL},
{"FM_CPU_ERR0_LVT3_N", "GPIOF0", GPIO_EDGE_FALLING, cpu_event_handler, NULL},
{"FM_CPU_ERR1_LVT3_N", "GPIOF1", GPIO_EDGE_FALLING, cpu_event_handler, NULL},
{"FM_CPU_ERR2_LVT3_N", "GPIOF2", GPIO_EDGE_FALLING, cpu_event_handler, NULL},
{"FM_MEM_THERM_EVENT_CPU0_LVT3_N", "GPIOB0", GPIO_EDGE_FALLING, mem_thermtrip_handler, NULL},
{"FM_MEM_THERM_EVENT_CPU1_LVT3_N", "GPIOB1", GPIO_EDGE_FALLING, mem_thermtrip_handler, NULL},
{"FM_SYS_THROTTLE_LVC3", "GPIOR7", GPIO_EDGE_BOTH, gpio_event_pson_handler, NULL},
{"IRQ_DIMM_SAVE_LVT3_N", "GPION4", GPIO_EDGE_BOTH, gpio_event_pson_handler, NULL},
{"FM_HSC_TIMER_EXP_N", "GPIOM2", GPIO_EDGE_BOTH, hsc_timer_exp_handler, NULL},
{"FM_CPU0_PROCHOT_LVT3_BMC_N", "GPIOB6", GPIO_EDGE_BOTH, cpu_prochot_handler, NULL},
{"FM_CPU1_PROCHOT_LVT3_BMC_N", "GPIOB7", GPIO_EDGE_BOTH, cpu_prochot_handler, NULL},
{"FM_PVCCIN_CPU0_PWR_IN_ALERT_N", "GPIOAA2", GPIO_EDGE_FALLING, cpu0_pvccin_pwr_in_handler, NULL},
{"FM_PVCCIN_CPU1_PWR_IN_ALERT_N", "GPIOAA3", GPIO_EDGE_FALLING, cpu1_pvccin_pwr_in_handler, NULL},
{"PWRGD_CPU0_LVC3", "GPIOZ1", GPIO_EDGE_BOTH, cpu_pwr_handler, init_cpu_pwr},
{"PRSNT_UPI_BD_1_N", "GPIOO5", GPIO_EDGE_BOTH, upi_detect_handler, upi_init_handler},
{"PRSNT_UPI_BD_2_N", "GPIOO6", GPIO_EDGE_BOTH, upi_detect_handler, upi_init_handler},
{"PRSNT_UPI_BD_3_N", "GPIOO7", GPIO_EDGE_BOTH, upi_detect_handler, upi_init_handler},
{"PRSNT_UPI_BD_4_N", "GPIOP0", GPIO_EDGE_BOTH, upi_detect_handler, upi_init_handler},
};
int main(int argc, char **argv)
{
int rc, pid_file;
gpiopoll_desc_t *polldesc;
pthread_t tid_ierr_mcerr_event;
pthread_t tid_gpio_timer;
pthread_t tid_ioex0_monitor;
pthread_t tid_power_fail_monitor;
pid_file = open("/var/run/gpiod.pid", O_CREAT | O_RDWR, 0666);
rc = flock(pid_file, LOCK_EX | LOCK_NB);
if (rc) {
if (EWOULDBLOCK == errno) {
syslog(LOG_ERR, "Another gpiod instance is running...\n");
exit(-1);
}
} else {
openlog("gpiod", LOG_CONS, LOG_DAEMON);
syslog(LOG_INFO, "gpiod: daemon started");
//Create thread for IERR/MCERR event detect
if (pthread_create(&tid_ierr_mcerr_event, NULL, ierr_mcerr_event_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for ierr_mcerr_event_handler\n");
exit(1);
}
//Create thread for platform reset event filter check
if (pthread_create(&tid_gpio_timer, NULL, gpio_timer, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for platform_reset_filter_handler\n");
exit(1);
}
if (pthread_create(&tid_ioex0_monitor, NULL, ioex0_monitor, NULL) < 0) {
syslog(LOG_CRIT, "FRU: %d pthread_create for io expender monitor error", FRU_MB);
exit(1);
}
if (pthread_create(&tid_power_fail_monitor, NULL, power_fail_monitor, NULL) < 0) {
syslog(LOG_CRIT, "FRU: %d pthread_create for power fail monitor error", FRU_MB);
exit(1);
}
polldesc = gpio_poll_open(g_gpios, sizeof(g_gpios)/sizeof(g_gpios[0]));
if (!polldesc) {
syslog(LOG_CRIT, "FRU: %d Cannot start poll operation on GPIOs", FRU_MB);
} else {
if (gpio_poll(polldesc, POLL_TIMEOUT)) {
syslog(LOG_CRIT, "FRU: %d Poll returned error", FRU_MB);
}
gpio_poll_close(polldesc);
}
}
return 0;
}