meta-facebook/meta-fbep/recipes-fbep/gpiod/files/gpiod.c (693 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 <pthread.h>
#include <sys/types.h>
#include <sys/file.h>
#include <openbmc/pal.h>
#include <openbmc/obmc-i2c.h>
#include <openbmc/libgpio.h>
#include <facebook/asic.h>
#define MAIN_CPLD_BUS (4)
#define MAIN_CPLD_ADDR (0x84)
#define CPLD_EVENT_REG (0xFE)
#define CPLD_CLEAR_REG (0xFF)
/*
* Note:
* CPLD confirmed these events won't be triggered at the same time
* We don't have to handle race condition
*/
#define THERMTRIP_CTRL (0x1 << 2)
#define RT_PWR_FAIL_CTRL (0x1 << 1)
#define BT_PWR_FAIL_CTRL (0x1 << 0)
#define LTC4282_REG_FAULT_LOG 0x04
#define FET_BAD_FAULT (0x1 << 6)
#define FET_SHORT_FAULT (0x1 << 5)
#define ON_FAULT (0x1 << 4)
#define POWER_BAD_FAULT (0x1 << 3)
#define OC_FAULT (0x1 << 2)
#define UV_FAULT (0x1 << 1)
#define OV_FAULT (0x1 << 0)
#define LTC4282_REG_ALERT_LOG 0x05
#define VSENSE_ALARM_HIGH (0x1 << 5)
#define ADM127x_REG_STATUS_IOUT 0x7B
#define IOUT_OC_FAULT (0x1 << 7)
#define IOUT_OC_WARN (0x1 << 5)
#define ADM127x_REG_STATUS_INPUT 0x7C
#define VIN_UV_WARN (0x1 << 5)
#define VIN_UV_FAULT (0x1 << 4)
#define POLL_TIMEOUT -1 /* Forever */
enum {
ERR_HSC_1_ALERT = 0,
ERR_HSC_2_ALERT,
ERR_HSC_AUX_ALERT,
ERR_HSC_1_THROT,
ERR_HSC_2_THROT,
ERR_ASIC_01_ALERT,
ERR_ASIC_23_ALERT,
ERR_ASIC_45_ALERT,
ERR_ASIC_67_ALERT,
ERR_ASIC_THERMTRIP,
ERR_PAX_0_ALERT,
ERR_PAX_1_ALERT,
ERR_PAX_2_ALERT,
ERR_PAX_3_ALERT,
ERR_CPLD_ALERT,
ERR_UNKNOWN
};
enum {
RUNTIME = 0,
BOOTUP
};
pthread_mutex_t led_mutex = PTHREAD_MUTEX_INITIALIZER;
static void log_gpio_change(gpiopoll_pin_t *gp, gpio_value_t value, useconds_t log_delay, bool low_active)
{
const struct gpiopoll_config *cfg = gpio_poll_get_config(gp);
assert(cfg);
if (low_active)
syslog(LOG_CRIT, "%s: %s - %s\n", value ? "DEASSERT": "ASSERT", cfg->description, cfg->shadow);
else
syslog(LOG_CRIT, "%s: %s - %s\n", value ? "ASSERT": "DEASSERT", cfg->description, cfg->shadow);
}
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, "Open failed for GPIO: %s\n", shadow);
return GPIO_VALUE_INVALID;
}
if (gpio_get_value(desc, &value)) {
syslog(LOG_CRIT, "Get failed for GPIO: %s\n", shadow);
value = GPIO_VALUE_INVALID;
}
gpio_close(desc);
return value;
}
static int set_dbg_led(uint8_t num)
{
const char *shadows[] = {
"LED_POSTCODE_0", "LED_POSTCODE_1", "LED_POSTCODE_2", "LED_POSTCODE_3",
"LED_POSTCODE_4", "LED_POSTCODE_5", "LED_POSTCODE_6", "LED_POSTCODE_7"
};
gpio_desc_t *gpio;
for (int i = 0; i < 8; i++) {
gpio = gpio_open_by_shadow(shadows[i]);
if (!gpio) {
syslog(LOG_WARNING, "%s: Open GPIO %s failed", __func__, shadows[i]);
return -1;
}
if (gpio_set_value(gpio, (num & (0x1 << i))? GPIO_VALUE_HIGH: GPIO_VALUE_LOW) < 0) {
syslog(LOG_WARNING, "%s: Set GPIO %s failed", __func__, shadows[i]);
gpio_close(gpio);
return -1;
}
gpio_close(gpio);
}
return 0;
}
static int sync_dbg_led(uint8_t err_bit, bool assert)
{
static int err = 0;
static uint8_t err_code[] = {
0x01, // ERR_HSC_1_ALERT
0x02, // ERR_HSC_2_ALERT
0x03, // ERR_HSC_AUX_ALERT
0x04, // ERR_HSC_1_THROT
0x05, // ERR_HSC_2_THROT
0x10, // ERR_ASIC_01_ALERT
0x11, // ERR_ASIC_23_ALERT
0x12, // ERR_ASIC_45_ALERT
0x13, // ERR_ASIC_67_ALERT
0x14, // ERR_ASIC_THERMTRIP
0x20, // ERR_PAX_0_ALERT
0x21, // ERR_PAX_1_ALERT
0x22, // ERR_PAX_2_ALERT
0x23, // ERR_PAX_3_ALERT
0x30, // ERR_CPLD_ALERT
};
pthread_mutex_lock(&led_mutex);
if (assert)
err |= (0x1 << err_bit);
else
err &= ~(0x1 << err_bit);
pthread_mutex_unlock(&led_mutex);
for (int i = 0; i < ERR_UNKNOWN; i++) {
if (err & (0x1 << i))
return set_dbg_led(err_code[i]);
}
return set_dbg_led(0x00);
}
int check_power_seq(int type)
{
struct power_seq {
char *name;
uint8_t offset;
uint8_t bit;
};
struct power_seq *cpld_power_seq;
struct power_seq runtime_seq[] = {
{"RT_MODULE_PWRGD_ASIC3_R", 0, 0}, {"RT_MODULE_PWRGD_ASIC4_R", 0, 1},
{"RT_MODULE_PWRGD_ASIC5_R", 0, 2}, {"RT_MODULE_PWRGD_ASIC6_R", 0, 3},
{"RT_MODULE_PWRGD_ASIC7_R", 0, 4}, {"RT_P12V_HSC_PG_R", 1, 0},
{"RT_VICOR_PG_R", 1, 1}, {"RT_P48V_HSC_PG_R", 1, 2},
{"RT_PW_PAX_POWER_GOOD", 1, 3}, {"RT_ASIC_ALL_DONE", 1, 4},
{"RT_MODULE_PWRGD_ASIC0", 1, 5}, {"RT_MODULE_PWRGD_ASIC1_R", 1, 6},
{"RT_MODULE_PWRGD_ASIC2_R", 1, 7}
};
struct power_seq boot_seq[] = {
{"BT_MODULE_PWRGD_ASIC0", 0, 0}, {"BT_MODULE_PWRGD_ASIC1", 0, 1},
{"BT_MODULE_PWRGD_ASIC2", 0, 2}, {"BT_MODULE_PWRGD_ASIC3", 0, 3},
{"BT_MODULE_PWRGD_ASIC4", 0, 4}, {"BT_MODULE_PWRGD_ASIC5", 0, 5},
{"BT_MODULE_PWRGD_ASIC6", 0, 6}, {"BT_MODULE_PWRGD_ASIC7", 0, 7},
{"BT_RST_CPLD_RSMRST_N", 1, 0}, {"BT_PWRGD_P5V_STBY_R", 1, 1},
{"BT_PWRGD_CPLD_VREFF", 1, 2}, {"BT_PWRGD_P2V5_AUX_R", 1, 3},
{"BT_PWRGD_P1V2_AUX_R", 1, 4}, {"BT_PWRGD_P1V15_AUX_R", 1, 5},
{"BT_P12V_HSC_PG_R_SYN2", 1, 6}, {"BT_VICOR_PG_R", 1, 7},
{"BT_P48V_HSC_PG_R_SYN2", 2, 0}, {"BT_PWRGD_P3V3_CPLD_R", 2, 1},
{"BT_HOST_PWRGD_ASIC", 2, 2}, {"BT_PW_PAX_POWER_GOOD", 2, 3},
{"BT_ASIC_ALL_DONE", 2, 4}
};
char dev_cpld[16] = {0};
char event_str[64] = {0};
int fd, i;
int ret = 0, fail_addr = -1;
uint8_t tbuf[8], rbuf[8], value;
uint8_t event_reg, state_reg, ctrl_offset;
uint8_t power_seq_num, read_bytes;
if (type == RUNTIME) {
cpld_power_seq = runtime_seq;
event_reg = 0x2b;
state_reg = 0x2b;
ctrl_offset = RT_PWR_FAIL_CTRL;
read_bytes = 2;
power_seq_num = 13;
} else {
cpld_power_seq = boot_seq;
event_reg = 0x2f;
state_reg = 0x2d;
ctrl_offset = BT_PWR_FAIL_CTRL;
read_bytes = 3;
power_seq_num = 21;
}
sprintf(dev_cpld, "/dev/i2c-%d", MAIN_CPLD_BUS);
fd = open(dev_cpld, O_RDWR);
if (fd < 0) {
return -1;
}
tbuf[0] = event_reg;
ret = i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, 1);
if (ret < 0 || (rbuf[0] & 0x80) == 0x0) // Read error or power is turned off normally
goto exit;
tbuf[0] = state_reg;
ret = i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, read_bytes);
if (ret < 0)
goto exit;
for (i = 0; i < power_seq_num; i++) {
value = rbuf[cpld_power_seq[i].offset] & (1 << cpld_power_seq[i].bit);
if (value == 0) {
fail_addr = i;
snprintf(event_str, sizeof(event_str), "%s power rail fails", cpld_power_seq[i].name);
syslog(LOG_CRIT, "%s", event_str);
pal_add_cri_sel(event_str);
}
}
if (fail_addr < 0) {
snprintf(event_str, sizeof(event_str), "Unknown power rail fails");
syslog(LOG_CRIT, "Unknown power rail fails");
pal_add_cri_sel(event_str);
}
if (type == BOOTUP) {
tbuf[0] = 0x31; // CPLD state
ret = i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, 1);
if (ret < 0)
goto exit;
syslog(LOG_CRIT, "CPLD bootup failed at state %d", rbuf[0]);
}
// 2s delay to prevent from triggering new alert while clearing event due to CPLD bug
sleep(2);
// Enable clear
tbuf[0] = CPLD_EVENT_REG;
tbuf[1] = ctrl_offset;
i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 2, rbuf, 0);
// Clear event
tbuf[0] = CPLD_CLEAR_REG;
i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, 1);
// Disable clear
tbuf[0] = CPLD_EVENT_REG;
tbuf[1] = 0x0;
i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 2, rbuf, 0);
exit:
close(fd);
return ret;
}
int check_pwr_brake()
{
const char* cpld_power_brake[] = {
"PWRBRK_ASIC0", "PWRBRK_ASIC1",
"PWRBRK_ASIC2", "PWRBRK_ASIC3",
"PWRBRK_ASIC4", "PWRBRK_ASIC5",
"PWRBRK_ASIC6", "PWRBRK_ASIC7"
};
char dev_cpld[16] = {0};
char event_str[64] = {0};
int fd, i;
int ret = 0, fail_addr = -1;
uint8_t tbuf[8], rbuf[8], value;
// Check if power is on
if (pal_is_server_off())
return 0;
sprintf(dev_cpld, "/dev/i2c-%d", MAIN_CPLD_BUS);
fd = open(dev_cpld, O_RDWR);
if (fd < 0) {
return -1;
}
tbuf[0] = 0x11; // Power Brake state
ret = i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, 1);
if (ret < 0)
goto exit;
for (i = 0; i < 8; i++) {
if (!is_asic_prsnt(i))
continue;
value = rbuf[0] & (0x1 << i);
if (value == 0) {
fail_addr = i;
snprintf(event_str, sizeof(event_str), "%s power brake", cpld_power_brake[i]);
syslog(LOG_CRIT, "%s", event_str);
}
}
if (fail_addr < 0) {
snprintf(event_str, sizeof(event_str), "Unknown GPU power brake");
syslog(LOG_CRIT, "Unknown GPU power brake");
}
exit:
close(fd);
return ret;
}
int check_hsc_alert(gpiopoll_pin_t *gp, int bus_id, uint8_t addr, uint8_t reg)
{
char dev[16] = {0};
int fd, ret;
uint8_t tbuf[8], rbuf[8];
const struct gpiopoll_config *cfg = gpio_poll_get_config(gp);
assert(cfg);
sprintf(dev, "/dev/i2c-%d", bus_id);
fd = open(dev, O_RDWR);
if (fd < 0) {
return -1;
}
tbuf[0] = reg;
ret = i2c_rdwr_msg_transfer(fd, addr, tbuf, 1, rbuf, 1);
close(fd);
if (ret < 0)
return -1;
switch (reg)
{
case LTC4282_REG_FAULT_LOG:
if (rbuf[0] & FET_BAD_FAULT)
syslog(LOG_CRIT, "%s: FET-BAD", cfg->description);
if (rbuf[0] & FET_SHORT_FAULT)
syslog(LOG_CRIT, "%s: FET-short", cfg->description);
if (rbuf[0] & ON_FAULT)
syslog(LOG_CRIT, "%s: ON pin changing", cfg->description);
if (rbuf[0] & POWER_BAD_FAULT)
syslog(LOG_CRIT, "%s: Power-bad", cfg->description);
if (rbuf[0] & OC_FAULT)
syslog(LOG_CRIT, "%s: Overcurrent", cfg->description);
if (rbuf[0] & UV_FAULT)
syslog(LOG_CRIT, "%s: Undervoltage", cfg->description);
if (rbuf[0] & OV_FAULT)
syslog(LOG_CRIT, "%s: Overvoltage", cfg->description);
break;
case LTC4282_REG_ALERT_LOG:
if (rbuf[0] & VSENSE_ALARM_HIGH)
syslog(LOG_CRIT, "%s: Overcurrent warning", cfg->description);
break;
case ADM127x_REG_STATUS_IOUT:
if (rbuf[0] & IOUT_OC_FAULT)
syslog(LOG_CRIT, "%s: Overcurrent fault", cfg->description);
if (rbuf[0] & IOUT_OC_WARN)
syslog(LOG_CRIT, "%s: Overcurrent warning", cfg->description);
break;
case ADM127x_REG_STATUS_INPUT:
if (rbuf[0] & VIN_UV_FAULT)
syslog(LOG_CRIT, "%s: Undervoltage fault", cfg->description);
if (rbuf[0] & VIN_UV_WARN)
syslog(LOG_CRIT, "%s: Undervoltage warning", cfg->description);
break;
default:
break;
}
return 0;
}
static bool is_gpu_thermal_trip()
{
bool thermtrip[8] = {false, false, false, false, false, false, false, false};
bool event = false;
int fd, ret;
char shadow[16] = {0};
char dev_cpld[16] = {0};
uint8_t tbuf[8], rbuf[8];
for (uint8_t i = 0; i < 8; i++) {
if (!is_asic_prsnt(i))
continue;
snprintf(shadow, sizeof(shadow), "OAM%d_THERMTRIP", (int)i);
if (gpio_get(shadow) == GPIO_VALUE_LOW)
thermtrip[i] = true;
}
sprintf(dev_cpld, "/dev/i2c-%d", MAIN_CPLD_BUS);
fd = open(dev_cpld, O_RDWR);
if (fd < 0) {
return false;
}
tbuf[0] = 0x0a; // Themral trip event
ret = i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, 1);
if (ret < 0)
goto exit;
event = rbuf[0] & 0x80? true: false;
// 250ms delay to avoid false alart during AC cycle due to hardware issue
msleep(250);
if (event) {
for (int i = 0; i < 8; i++) {
if (thermtrip[i])
syslog(LOG_CRIT, "ASIC%d Thermal Trip\n", i);
}
}
// Enable clear
tbuf[0] = CPLD_EVENT_REG;
tbuf[1] = THERMTRIP_CTRL;
i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 2, rbuf, 0);
// Clear event
tbuf[0] = CPLD_CLEAR_REG;
i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 1, rbuf, 1);
// Disable clear
tbuf[0] = CPLD_EVENT_REG;
tbuf[1] = 0x0;
i2c_rdwr_msg_transfer(fd, MAIN_CPLD_ADDR, tbuf, 2, rbuf, 0);
exit:
close(fd);
return event;
}
// Generic Event Handler for GPIO changes
static void gpio_event_handle_low_active(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
log_gpio_change(gp, curr, 0, true);
}
static void gpio_event_handle_high_active(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
log_gpio_change(gp, curr, 0, false);
}
static void gpio_event_handle_pwr_brake(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (gpio_get("OAM_FAST_BRK_ON_N") == GPIO_VALUE_LOW &&
curr == GPIO_VALUE_LOW && check_pwr_brake() < 0) {
syslog(LOG_WARNING, "Failed to get power brake state from CPLD");
}
gpio_event_handle_low_active(gp, last, curr);
}
static void gpio_event_handle_pwr_good(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
// 250ms delay to avoid false alart during AC cycle due to hardware issue
msleep(250);
if (curr == GPIO_VALUE_LOW && check_power_seq(RUNTIME) < 0)
syslog(LOG_WARNING, "Failed to get power state from CPLD");
gpio_event_handle_low_active(gp, last, curr);
}
// Specific Event Handlers
static void gpio_event_handle_power_btn(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
log_gpio_change(gp, curr, 0, true);
}
static void asic_def_prsnt(gpiopoll_pin_t *gp, gpio_value_t curr)
{
if (curr == GPIO_VALUE_HIGH)
log_gpio_change(gp, curr, 0, false);
}
static void gpio_event_hsc_1_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
gpio_event_handle_pwr_brake(gp, last, curr);
sync_dbg_led(ERR_HSC_1_ALERT, curr == GPIO_VALUE_LOW? true: false);
if (curr == GPIO_VALUE_LOW) {
if (check_hsc_alert(gp, 16, 0x53 << 1, LTC4282_REG_ALERT_LOG) < 0)
syslog(LOG_WARNING, "Failed to get alert status from P12V HSC_1");
if (!pal_is_server_off() &&
(check_hsc_alert(gp, 16, 0x13 << 1, ADM127x_REG_STATUS_IOUT) < 0 ||
check_hsc_alert(gp, 16, 0x13 << 1, ADM127x_REG_STATUS_INPUT) < 0)) {
syslog(LOG_WARNING, "Failed to get alert status from P48V HSC_1");
}
}
}
static void gpio_event_hsc_2_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
gpio_event_handle_pwr_brake(gp, last, curr);
sync_dbg_led(ERR_HSC_2_ALERT, curr == GPIO_VALUE_LOW? true: false);
if (curr == GPIO_VALUE_LOW) {
if (check_hsc_alert(gp, 17, 0x40 << 1, LTC4282_REG_ALERT_LOG) < 0)
syslog(LOG_WARNING, "Failed to get alert status from P12V HSC_2");
if (!pal_is_server_off() &&
(check_hsc_alert(gp, 17, 0x10 << 1, ADM127x_REG_STATUS_IOUT) < 0 ||
check_hsc_alert(gp, 17, 0x10 << 1, ADM127x_REG_STATUS_INPUT) < 0)) {
syslog(LOG_WARNING, "Failed to get alert status from P48V HSC_2");
}
}
}
static void gpio_event_hsc_aux_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
gpio_event_handle_pwr_brake(gp, last, curr);
sync_dbg_led(ERR_HSC_AUX_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_hsc_1_throt(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
gpio_event_handle_pwr_brake(gp, last, curr);
sync_dbg_led(ERR_HSC_1_THROT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_hsc_2_throt(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
gpio_event_handle_pwr_brake(gp, last, curr);
sync_dbg_led(ERR_HSC_2_THROT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_asic01_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_ASIC_01_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_asic23_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_ASIC_23_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_asic45_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_ASIC_45_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_asic67_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_ASIC_67_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_pax_0_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
if (pal_is_fw_update_ongoing(FRU_MB))
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_PAX_0_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_pax_1_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
if (pal_is_fw_update_ongoing(FRU_MB))
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_PAX_1_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_pax_2_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
if (pal_is_fw_update_ongoing(FRU_MB))
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_PAX_2_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_pax_3_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (pal_is_server_off())
return;
if (pal_is_fw_update_ongoing(FRU_MB))
return;
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_PAX_3_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void gpio_event_cpld_alert(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
// 250ms delay to avoid false alart during AC cycle due to hardware issue
msleep(250);
if (curr == GPIO_VALUE_LOW && check_power_seq(BOOTUP) < 0)
syslog(LOG_WARNING, "Failed to get power state from CPLD");
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_CPLD_ALERT, curr == GPIO_VALUE_LOW? true: false);
}
static void cpld_def_alert(gpiopoll_pin_t *gp, gpio_value_t curr)
{
if (curr == GPIO_VALUE_LOW) {
if (check_power_seq(BOOTUP) < 0)
syslog(LOG_WARNING, "Failed to get power state from CPLD");
log_gpio_change(gp, curr, 0, false);
sync_dbg_led(ERR_CPLD_ALERT, true);
}
}
static void gpio_event_asic_thermtrip(gpiopoll_pin_t *gp, gpio_value_t last, gpio_value_t curr)
{
if (!pal_is_server_off() && is_gpu_thermal_trip()) {
gpio_event_handle_low_active(gp, last, curr);
sync_dbg_led(ERR_ASIC_THERMTRIP, curr == GPIO_VALUE_LOW? true: false);
}
}
// GPIO table to be monitored
static struct gpiopoll_config g_gpios[] = {
// shadow, description, edge, handler, oneshot
{"BMC_PWR_BTN_IN_N", "Power button", GPIO_EDGE_BOTH, gpio_event_handle_power_btn, NULL},
{"SYS_PWR_READY", "System power off", GPIO_EDGE_BOTH, gpio_event_handle_pwr_good, NULL},
{"PMBUS_BMC_1_ALERT_N", "HSC 1 Alert", GPIO_EDGE_BOTH, gpio_event_hsc_1_alert, NULL},
{"PMBUS_BMC_2_ALERT_N", "HSC 2 Alert", GPIO_EDGE_BOTH, gpio_event_hsc_2_alert, NULL},
{"PMBUS_BMC_3_ALERT_N", "HSC AUX Alert", GPIO_EDGE_BOTH, gpio_event_hsc_aux_alert, NULL},
{"HSC1_THROT_N", "HSC 1 Throttle", GPIO_EDGE_BOTH, gpio_event_hsc_1_throt, NULL},
{"HSC2_THROT_N", "HSC 2 Throttle", GPIO_EDGE_BOTH, gpio_event_hsc_2_throt, NULL},
{"SMB_ALERT_ASIC01", "ASIC01 Alert", GPIO_EDGE_BOTH, gpio_event_asic01_alert, NULL},
{"SMB_ALERT_ASIC23", "ASIC23 Alert", GPIO_EDGE_BOTH, gpio_event_asic23_alert, NULL},
{"SMB_ALERT_ASIC45", "ASIC45 Alert", GPIO_EDGE_BOTH, gpio_event_asic45_alert, NULL},
{"SMB_ALERT_ASIC67", "ASIC67 Alert", GPIO_EDGE_BOTH, gpio_event_asic67_alert, NULL},
{"THERMTRIP_N_ASIC07", "ASIC Thermal Trip", GPIO_EDGE_BOTH, gpio_event_asic_thermtrip, NULL},
{"CPLD_SMB_ALERT_N", "CPLD Alert", GPIO_EDGE_BOTH, gpio_event_cpld_alert, cpld_def_alert},
{"PAX0_ALERT", "PAX0 Alert", GPIO_EDGE_BOTH, gpio_event_pax_0_alert, NULL},
{"PAX1_ALERT", "PAX1 Alert", GPIO_EDGE_BOTH, gpio_event_pax_1_alert, NULL},
{"PAX2_ALERT", "PAX2 Alert", GPIO_EDGE_BOTH, gpio_event_pax_2_alert, NULL},
{"PAX3_ALERT", "PAX3 Alert", GPIO_EDGE_BOTH, gpio_event_pax_3_alert, NULL},
{"PRSNT0_N_ASIC0", "ASIC0 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC0", "ASIC0 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC1", "ASIC1 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC1", "ASIC1 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC2", "ASIC2 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC2", "ASIC2 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC3", "ASIC3 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC3", "ASIC3 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC4", "ASIC4 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC4", "ASIC4 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC5", "ASIC5 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC5", "ASIC5 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC6", "ASIC6 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC6", "ASIC6 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT0_N_ASIC7", "ASIC7 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
{"PRSNT1_N_ASIC7", "ASIC7 present off", GPIO_EDGE_RISING, gpio_event_handle_high_active, asic_def_prsnt},
};
// For monitoring GPIOs on IO expender
struct gpioexppoll_config {
char shadow[64];
char description[64];
gpio_value_t last;
gpio_value_t curr;
};
static void* fan_status_monitor()
{
int i;
struct gpioexppoll_config fan_gpios[] = {
{"FAN0_PRESENT", "Fan0 present", GPIO_VALUE_LOW, GPIO_VALUE_INVALID},
{"FAN1_PRESENT", "Fan1 present", GPIO_VALUE_LOW, GPIO_VALUE_INVALID},
{"FAN2_PRESENT", "Fan2 present", GPIO_VALUE_LOW, GPIO_VALUE_INVALID},
{"FAN3_PRESENT", "Fan3 present", GPIO_VALUE_LOW, GPIO_VALUE_INVALID},
{"FAN0_PWR_GOOD", "Fan0 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"FAN1_PWR_GOOD", "Fan1 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"FAN2_PWR_GOOD", "Fan2 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"FAN3_PWR_GOOD", "Fan3 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
};
struct gpioexppoll_config *gp;
while (1) {
sleep(1);
for (i = 0; i < 8; i++) {
if (pal_is_server_off())
continue;
gp = &fan_gpios[i];
if (i>>2)
gp->curr = gpio_get(gp->shadow);
else
gp->curr = pal_is_fan_prsnt(i<<1)? GPIO_VALUE_LOW: GPIO_VALUE_HIGH;
if (gp->last != gp->curr) {
if (i>>2) {
syslog(LOG_CRIT, "%s: %s - %s\n",
gp->curr == GPIO_VALUE_HIGH? "ON": "OFF",
gp->description,
gp->shadow);
} else {
syslog(LOG_CRIT, "%s: %s - %s\n",
gp->curr == GPIO_VALUE_LOW? "ON": "OFF",
gp->description,
gp->shadow);
}
gp->last = gp->curr;
}
}
}
return NULL;
}
static void* asic_status_monitor()
{
int i;
struct gpioexppoll_config asic_gpios[] = {
{"OAM0_MODULE_PWRGD", "ASIC0 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM1_MODULE_PWRGD", "ASIC1 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM2_MODULE_PWRGD", "ASIC2 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM3_MODULE_PWRGD", "ASIC3 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM4_MODULE_PWRGD", "ASIC4 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM5_MODULE_PWRGD", "ASIC5 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM6_MODULE_PWRGD", "ASIC6 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
{"OAM7_MODULE_PWRGD", "ASIC7 power", GPIO_VALUE_HIGH, GPIO_VALUE_INVALID},
};
struct gpioexppoll_config *gp;
while (1) {
sleep(1);
for (i = 0; i < 8; i++) {
if (pal_is_server_off() || !is_asic_prsnt(i))
continue;
gp = &asic_gpios[i];
gp->curr = gpio_get(gp->shadow);
if (gp->last != gp->curr) {
syslog(LOG_CRIT, "%s: %s - %s\n",
gp->curr == GPIO_VALUE_HIGH? "ON": "OFF",
gp->description,
gp->shadow);
}
gp->last = gp->curr;
}
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t tid_fan_monitor;
pthread_t tid_asic_monitor;
int rc, pid_file;
gpiopoll_desc_t *polldesc;
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");
if (pthread_create(&tid_fan_monitor, NULL, fan_status_monitor, NULL) < 0) {
syslog(LOG_CRIT, "pthread_create for fan monitor error");
exit(1);
}
if (pthread_create(&tid_asic_monitor, NULL, asic_status_monitor, NULL) < 0) {
syslog(LOG_CRIT, "pthread_create for asic monitor error");
exit(1);
}
polldesc = gpio_poll_open(g_gpios, sizeof(g_gpios)/sizeof(g_gpios[0]));
if (!polldesc) {
syslog(LOG_CRIT, "Cannot start poll operation on GPIOs");
} else {
if (gpio_poll(polldesc, POLL_TIMEOUT)) {
syslog(LOG_CRIT, "Poll returned error");
}
gpio_poll_close(polldesc);
}
}
return 0;
}