blob: afa0914307d14defcfb9eccb6a35713f56fcb3ae [file] [log] [blame]
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <argp.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dirent.h>
#include "openbmc_intf.h"
#include "gpio.h"
#include "gpio_json.h"
#include <sys/ioctl.h>
#include <linux/gpio.h>
#define GPIO_PORT_OFFSET 8
#define GPIO_BASE_PATH "/sys/class/gpio"
cJSON* gpio_json = NULL;
int gpio_write(GPIO* gpio, uint8_t value)
{
g_assert (gpio != NULL);
struct gpiohandle_data data;
memset(&data, 0, sizeof(data));
data.values[0] = value;
if (gpio->fd <= 0)
{
return GPIO_ERROR;
}
if (ioctl(gpio->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0)
{
return GPIO_WRITE_ERROR;
}
return GPIO_OK;
}
int gpio_read(GPIO* gpio, uint8_t *value)
{
g_assert (gpio != NULL);
struct gpiohandle_data data;
memset(&data, 0, sizeof(data));
if (gpio->fd <= 0)
{
return GPIO_ERROR;
}
if (ioctl(gpio->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0)
{
return GPIO_READ_ERROR;
}
*value = data.values[0];
return GPIO_OK;
}
/**
* Determine the GPIO base number for the system. It is found in
* the 'base' file in the /sys/class/gpio/gpiochipX/ directory where the
* /sys/class/gpio/gpiochipX/label file has a '1e780000.gpio' in it.
*
* Note: This method is ASPEED specific. Could add support for
* additional SOCs in the future.
*
* @return int - the GPIO base number, or < 0 if not found
*/
int get_gpio_base()
{
int gpio_base = -1;
DIR* dir = opendir(GPIO_BASE_PATH);
if (dir == NULL)
{
fprintf(stderr, "Unable to open directory %s\n",
GPIO_BASE_PATH);
return -1;
}
struct dirent* entry;
while ((entry = readdir(dir)) != NULL)
{
/* Look in the gpiochip<X> directories for a file called 'label' */
/* that contains '1e780000.gpio', then in that directory read */
/* the GPIO base out of the 'base' file. */
if (strncmp(entry->d_name, "gpiochip", 8) != 0)
{
continue;
}
gboolean is_bmc = FALSE;
char* label_name;
asprintf(&label_name, "%s/%s/label",
GPIO_BASE_PATH, entry->d_name);
FILE* fd = fopen(label_name, "r");
free(label_name);
if (!fd)
{
continue;
}
char label[14];
if (fgets(label, 14, fd) != NULL)
{
if (strcmp(label, "1e780000.gpio") == 0)
{
is_bmc = TRUE;
}
}
fclose(fd);
if (!is_bmc)
{
continue;
}
char* base_name;
asprintf(&base_name, "%s/%s/base",
GPIO_BASE_PATH, entry->d_name);
fd = fopen(base_name, "r");
free(base_name);
if (!fd)
{
continue;
}
if (fscanf(fd, "%d", &gpio_base) != 1)
{
gpio_base = -1;
}
fclose(fd);
/* We found the right file. No need to continue. */
break;
}
closedir(dir);
if (gpio_base == -1)
{
fprintf(stderr, "Could not find GPIO base\n");
}
return gpio_base;
}
/**
* Converts the GPIO port/offset nomenclature value
* to a number. Supports the ASPEED method where
* num = base + (port * 8) + offset, and the port is an
* A-Z character that converts to 0 to 25. The base
* is obtained form the hardware.
*
* For example: "A5" -> 5, "Z7" -> 207
*
* @param[in] gpio - the GPIO name
*
* @return int - the GPIO number or < 0 if not found
*/
int convert_gpio_to_num(const char* gpio)
{
size_t len = strlen(gpio);
if (len < 2)
{
fprintf(stderr, ("Invalid GPIO name %s\n", gpio));
return -1;
}
/* Read the offset from the last character */
if (!isdigit(gpio[len-1]))
{
fprintf(stderr, "Invalid GPIO offset in GPIO %s\n", gpio);
return -1;
}
int offset = gpio[len-1] - '0';
/* Read the port from the second to last character */
if (!isalpha(gpio[len-2]))
{
fprintf(stderr, "Invalid GPIO port in GPIO %s\n", gpio);
return -1;
}
int port = toupper(gpio[len-2]) - 'A';
/* Check for a 2 character port, like AA */
if ((len == 3) && isalpha(gpio[len-3]))
{
port += 26 * (toupper(gpio[len-3]) - 'A' + 1);
}
return (port * GPIO_PORT_OFFSET) + offset;
}
/**
* Returns the cJSON pointer to the GPIO definition
* for the GPIO passed in.
*
* @param[in] gpio_name - the GPIO name, like BMC_POWER_UP
*
* @return cJSON* - pointer to the cJSON object or NULL
* if not found.
*/
cJSON* get_gpio_def(const char* gpio_name)
{
if (gpio_json == NULL)
{
gpio_json = load_json();
if (gpio_json == NULL)
{
return NULL;
}
}
cJSON* gpio_defs = cJSON_GetObjectItem(gpio_json, "gpio_definitions");
g_assert(gpio_defs != NULL);
cJSON* def;
cJSON_ArrayForEach(def, gpio_defs)
{
cJSON* name = cJSON_GetObjectItem(def, "name");
g_assert(name != NULL);
if (strcmp(name->valuestring, gpio_name) == 0)
{
return def;
}
}
return NULL;
}
/**
* Frees the gpio_json memory
*
* Can be called once when callers are done calling making calls
* to gpio_init() so that the JSON only needs to be loaded once.
*/
void gpio_inits_done()
{
if (gpio_json != NULL)
{
cJSON_Delete(gpio_json);
gpio_json = NULL;
}
}
/**
* Fills in the dev, direction, and num elements in
* the GPIO structure.
*
* @param gpio - the GPIO structure to fill in
*
* @return GPIO_OK if successful
*/
int gpio_get_params(GPIO* gpio)
{
gpio->dev = g_strdup(GPIO_BASE_PATH);
const cJSON* def = get_gpio_def(gpio->name);
if (def == NULL)
{
fprintf(stderr, "Unable to find GPIO %s in the JSON\n",
gpio->name);
return GPIO_LOOKUP_ERROR;
}
const cJSON* dir = cJSON_GetObjectItem(def, "direction");
g_assert(dir != NULL);
gpio->direction = g_strdup(dir->valuestring);
/* Must use either 'num', like 87, or 'pin', like "A5" */
const cJSON* num = cJSON_GetObjectItem(def, "num");
if ((num != NULL) && cJSON_IsNumber(num))
{
gpio->num = num->valueint;
}
else
{
const cJSON* pin = cJSON_GetObjectItem(def, "pin");
g_assert(pin != NULL);
gpio->num = convert_gpio_to_num(pin->valuestring);
if (gpio->num < 0)
{
return GPIO_LOOKUP_ERROR;
}
}
// TODO: For the purposes of skeleton and the projects that use it,
// it should be safe to assume this will always be 0. Eventually skeleton
// should be going away, but if the need arises before then this may need
// to be updated to handle non-zero cases.
gpio->chip_id = 0;
return GPIO_OK;
}
int gpio_open(GPIO* gpio, uint8_t default_value)
{
g_assert (gpio != NULL);
char buf[255];
sprintf(buf, "/dev/gpiochip%d", gpio->chip_id);
gpio->fd = open(buf, 0);
if (gpio->fd == -1)
{
return GPIO_OPEN_ERROR;
}
struct gpiohandle_request req;
memset(&req, 0, sizeof(req));
strncpy(req.consumer_label, "skeleton-gpio", sizeof(req.consumer_label));
// open gpio for writing or reading
if (gpio->direction == NULL)
{
gpio_close(gpio);
return GPIO_OPEN_ERROR;
}
req.flags = (strcmp(gpio->direction,"in") == 0) ? GPIOHANDLE_REQUEST_INPUT
: GPIOHANDLE_REQUEST_OUTPUT;
req.lineoffsets[0] = gpio->num;
req.lines = 1;
if (strcmp(gpio->direction,"out") == 0)
{
req.default_values[0] = default_value;
}
int rc = ioctl(gpio->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (rc < 0)
{
gpio_close(gpio);
return GPIO_OPEN_ERROR;
}
gpio_close(gpio);
gpio->fd = req.fd;
return GPIO_OK;
}
void gpio_close(GPIO* gpio)
{
if(gpio->fd < 0)
return;
close(gpio->fd);
gpio->fd = -1;
}