meta-facebook/meta-yosemite/recipes-yosemite/gpiod/files/gpiod.c (229 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 <stdbool.h> #include <unistd.h> #include <errno.h> #include <syslog.h> #include <stdint.h> #include <math.h> #include <string.h> #include <pthread.h> #include <sys/un.h> #include <sys/file.h> #include <openbmc/ipmi.h> #include <openbmc/pal.h> #include <facebook/bic.h> #include <facebook/yosemite_gpio.h> #define MAX_NUM_SLOTS 4 #define DELAY_GPIOD_READ 500000 // Polls each slot gpio values every 4*x usec #define SOCK_PATH_GPIO "/tmp/gpio_socket" #define PWR_UTL_LOCK "/var/run/power-util_%d.lock" /* To hold the gpio info and status */ typedef struct { uint8_t flag; uint8_t status; uint8_t ass_val; char name[32]; } gpio_pin_t; static gpio_pin_t gpio_slot1[MAX_GPIO_PINS] = {0}; static gpio_pin_t gpio_slot2[MAX_GPIO_PINS] = {0}; static gpio_pin_t gpio_slot3[MAX_GPIO_PINS] = {0}; static gpio_pin_t gpio_slot4[MAX_GPIO_PINS] = {0}; /* Returns the pointer to the struct holding all gpio info for the fru#. */ static gpio_pin_t * get_struct_gpio_pin(uint8_t fru) { gpio_pin_t *gpios; switch (fru) { case FRU_SLOT1: gpios = gpio_slot1; break; case FRU_SLOT2: gpios = gpio_slot2; break; case FRU_SLOT3: gpios = gpio_slot3; break; case FRU_SLOT4: gpios = gpio_slot4; break; default: syslog(LOG_WARNING, "get_struct_gpio_pin: Wrong SLOT ID %d\n", fru); return NULL; } return gpios; } static void populate_gpio_pins(uint8_t fru) { int i, ret; gpio_pin_t *gpios; gpios = get_struct_gpio_pin(fru); if (gpios == NULL) { syslog(LOG_WARNING, "populate_gpio_pins: get_struct_gpio_pin failed."); return; } // Only monitor the PWRGD_COREPWR pin gpios[PWRGOOD_CPU].flag = 1; for (i = 0; i < MAX_GPIO_PINS; i++) { if (gpios[i].flag) { gpios[i].ass_val = GETBIT(gpio_ass_val, i); ret = yosemite_get_gpio_name(fru, i, gpios[i].name); if (ret < 0) continue; } } } /* Wrapper function to configure and get all gpio info */ static void init_gpio_pins() { int fru; for (fru = FRU_SLOT1; fru < (FRU_SLOT1 + MAX_NUM_SLOTS); fru++) { populate_gpio_pins(fru); } } /* Monitor the gpio pins */ static int gpio_monitor_poll(uint8_t fru_flag) { int i, ret; uint8_t fru; uint8_t slot_12v[MAX_NUM_SLOTS + 1]; uint32_t revised_pins, n_pin_val, o_pin_val[MAX_NUM_SLOTS + 1] = {0}; gpio_pin_t *gpios; char pwr_state[MAX_VALUE_LEN]; char path[128]; uint32_t status; bic_gpio_t gpio = {0}; /* Check for initial Asserts */ for (fru = 1; fru <= MAX_NUM_SLOTS; fru++) { if (GETBIT(fru_flag, fru) == 0) continue; // Inform BIOS that BMC is ready bic_set_gpio(fru, BMC_READY_N, 0); ret = bic_get_gpio(fru, &gpio); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "gpio_monitor_poll: bic_get_gpio failed for " " fru %u", fru); #endif continue; } gpios = get_struct_gpio_pin(fru); if (gpios == NULL) { syslog(LOG_WARNING, "gpio_monitor_poll: get_struct_gpio_pin failed for" " fru %u", fru); continue; } memcpy(&status, (uint8_t *) &gpio, sizeof(status)); slot_12v[fru] = 1; o_pin_val[fru] = 0; for (i = 0; i < MAX_GPIO_PINS; i++) { if (gpios[i].flag == 0) continue; gpios[i].status = GETBIT(status, i); if (gpios[i].status) o_pin_val[fru] = SETBIT(o_pin_val[fru], i); } } /* Keep monitoring each fru's gpio pins every 4 * GPIOD_READ_DELAY seconds */ while(1) { for (fru = 1; fru <= MAX_NUM_SLOTS; fru++) { if (!(GETBIT(fru_flag, fru))) { usleep(DELAY_GPIOD_READ); continue; } if (slot_12v[fru] == 0) { // workaround, may get fake PWRGOOD_CPU status when slot12V is just turned on pal_is_server_12v_on(fru, &slot_12v[fru]); usleep(DELAY_GPIOD_READ); continue; } gpios = get_struct_gpio_pin(fru); if (gpios == NULL) { syslog(LOG_WARNING, "gpio_monitor_poll: get_struct_gpio_pin failed for" " fru %u", fru); continue; } memset(pwr_state, 0, MAX_VALUE_LEN); pal_get_last_pwr_state(fru, pwr_state); /* Get the GPIO pins */ if ((ret = bic_get_gpio(fru, (bic_gpio_t *) &n_pin_val)) < 0) { #ifdef DEBUG /* log the error message only when the CPU is on but not reachable. */ if (!(strcmp(pwr_state, "on"))) { syslog(LOG_WARNING, "gpio_monitor_poll: bic_get_gpio failed for fru %u", fru); } #endif if ((pal_is_server_12v_on(fru, &slot_12v[fru]) != 0) || slot_12v[fru]) { usleep(DELAY_GPIOD_READ); continue; } n_pin_val = CLEARBIT(o_pin_val[fru], PWRGOOD_CPU); } if (o_pin_val[fru] == n_pin_val) { usleep(DELAY_GPIOD_READ); continue; } revised_pins = (n_pin_val ^ o_pin_val[fru]); for (i = 0; i < MAX_GPIO_PINS; i++) { if (GETBIT(revised_pins, i) && (gpios[i].flag == 1)) { gpios[i].status = GETBIT(n_pin_val, i); // Check if the new GPIO val is ASSERT if (gpios[i].status == gpios[i].ass_val) { /* * GPIO - PWRGOOD_CPU assert indicates that the CPU is turned off or in a bad shape. * Raise an error and change the LPS from on to off or vice versa for deassert. */ if (strcmp(pwr_state, "off")) { if ((pal_is_server_12v_on(fru, &slot_12v[fru]) != 0) || slot_12v[fru]) { // Check if power-util is still running to ignore getting incorrect power status sprintf(path, PWR_UTL_LOCK, fru); if (access(path, F_OK) != 0) { pal_set_last_pwr_state(fru, "off"); } } } syslog(LOG_CRIT, "FRU: %d, System powered OFF", fru); // Inform BIOS that BMC is ready bic_set_gpio(fru, BMC_READY_N, 0); } else { if (strcmp(pwr_state, "on")) { if ((pal_is_server_12v_on(fru, &slot_12v[fru]) != 0) || slot_12v[fru]) { // Check if power-util is still running to ignore getting incorrect power status sprintf(path, PWR_UTL_LOCK, fru); if (access(path, F_OK) != 0) { pal_set_last_pwr_state(fru, "on"); } } } syslog(LOG_CRIT, "FRU: %d, System powered ON", fru); } } } o_pin_val[fru] = n_pin_val; usleep(DELAY_GPIOD_READ); } /* For Loop for each fru */ } /* while loop */ return 0; } /* function definition*/ static void print_usage() { printf("Usage: gpiod [ %s ]\n", pal_server_list); } /* Spawns a pthread for each fru to monitor all the sensors on it */ static void run_gpiod(int argc, char **argv) { int i, ret; uint8_t fru_flag, fru; /* Check for which fru do we need to monitor the gpio pins */ fru_flag = 0; for (i = 1; i < argc; i++) { ret = pal_get_fru_id(argv[i], &fru); if (ret < 0) { print_usage(); exit(-1); } fru_flag = SETBIT(fru_flag, fru); } gpio_monitor_poll(fru_flag); } int main(int argc, char **argv) { int rc, pid_file; if (argc < 2) { print_usage(); exit(-1); } 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) { printf("Another gpiod instance is running...\n"); exit(-1); } } else { init_gpio_pins(); daemon(0,1); openlog("gpiod", LOG_CONS, LOG_DAEMON); syslog(LOG_INFO, "gpiod: daemon started"); run_gpiod(argc, argv); } return 0; }