|
|
|
@@ -8,6 +8,8 @@
|
|
|
|
|
#include "gpio.h"
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
|
|
/* Those defines are used for legacy config files only */
|
|
|
|
|
#define GPIO_CHIP1_LABEL "1c20800.pinctrl"
|
|
|
|
@@ -40,11 +42,41 @@ static char *gpio_in_names[] = {
|
|
|
|
|
"status",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum gpiod_line_value gpio_line_get_value(struct EG25Manager *manager, int line) {
|
|
|
|
|
enum gpiod_line_value value;
|
|
|
|
|
unsigned int offset;
|
|
|
|
|
|
|
|
|
|
gpiod_line_request_get_requested_offsets(manager->gpio_in[line], &offset, 1);
|
|
|
|
|
value = gpiod_line_request_get_value(manager->gpio_in[line], offset);
|
|
|
|
|
|
|
|
|
|
if (value == GPIOD_LINE_VALUE_ERROR) {
|
|
|
|
|
g_warning("gpio: couldn't get value on line %d", line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int gpio_line_set_value(struct EG25Manager *manager, int line, enum gpiod_line_value value) {
|
|
|
|
|
unsigned int offset;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
gpiod_line_request_get_requested_offsets(manager->gpio_out[line], &offset, 1);
|
|
|
|
|
ret = gpiod_line_request_set_value(manager->gpio_out[line], offset, value);
|
|
|
|
|
if (ret) {
|
|
|
|
|
g_warning("gpio: couldn't set value %d on line %d", value, line);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
manager->gpio_out_value[line] = value;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int gpio_sequence_poweron(struct EG25Manager *manager)
|
|
|
|
|
{
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_ACTIVE);
|
|
|
|
|
sleep(1);
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_PWRKEY, GPIOD_LINE_VALUE_INACTIVE);
|
|
|
|
|
|
|
|
|
|
g_message("Executed power-on/off sequence");
|
|
|
|
|
|
|
|
|
@@ -53,7 +85,7 @@ int gpio_sequence_poweron(struct EG25Manager *manager)
|
|
|
|
|
|
|
|
|
|
int gpio_sequence_shutdown(struct EG25Manager *manager)
|
|
|
|
|
{
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DISABLE], 1);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_DISABLE, GPIOD_LINE_VALUE_ACTIVE);
|
|
|
|
|
gpio_sequence_poweron(manager);
|
|
|
|
|
|
|
|
|
|
g_message("Executed power-off sequence");
|
|
|
|
@@ -63,7 +95,7 @@ int gpio_sequence_shutdown(struct EG25Manager *manager)
|
|
|
|
|
|
|
|
|
|
int gpio_sequence_suspend(struct EG25Manager *manager)
|
|
|
|
|
{
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 1);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_APREADY, GPIOD_LINE_VALUE_ACTIVE);
|
|
|
|
|
|
|
|
|
|
g_message("Executed suspend sequence");
|
|
|
|
|
|
|
|
|
@@ -72,7 +104,7 @@ int gpio_sequence_suspend(struct EG25Manager *manager)
|
|
|
|
|
|
|
|
|
|
int gpio_sequence_resume(struct EG25Manager *manager)
|
|
|
|
|
{
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 0);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_APREADY, GPIOD_LINE_VALUE_INACTIVE);
|
|
|
|
|
|
|
|
|
|
g_message("Executed resume sequence");
|
|
|
|
|
|
|
|
|
@@ -81,8 +113,8 @@ int gpio_sequence_resume(struct EG25Manager *manager)
|
|
|
|
|
|
|
|
|
|
int gpio_sequence_wake(struct EG25Manager *manager)
|
|
|
|
|
{
|
|
|
|
|
if (gpiod_line_get_value(manager->gpio_out[GPIO_OUT_DTR])) {
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 0);
|
|
|
|
|
if (manager->gpio_out_value[GPIO_OUT_DTR]) {
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_INACTIVE);
|
|
|
|
|
|
|
|
|
|
/* Give the modem 200ms to wake from soft sleep */
|
|
|
|
|
usleep(200000);
|
|
|
|
@@ -95,42 +127,150 @@ int gpio_sequence_wake(struct EG25Manager *manager)
|
|
|
|
|
|
|
|
|
|
int gpio_sequence_sleep(struct EG25Manager *manager)
|
|
|
|
|
{
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 1);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_DTR, GPIOD_LINE_VALUE_ACTIVE);
|
|
|
|
|
g_message("Executed soft sleep sequence");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct gpiod_line *gpio_get_output_line(struct EG25Manager *manager, int chip, int line)
|
|
|
|
|
struct gpiod_line_request *gpio_request_line(struct EG25Manager *manager, int chip, unsigned int line, enum gpiod_line_direction direction) {
|
|
|
|
|
struct gpiod_line_request *request = NULL;
|
|
|
|
|
struct gpiod_line_settings *settings;
|
|
|
|
|
struct gpiod_line_config *line_cfg;
|
|
|
|
|
struct gpiod_request_config *req_cfg;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
settings = gpiod_line_settings_new();
|
|
|
|
|
if (!settings)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
gpiod_line_settings_set_direction(settings, direction);
|
|
|
|
|
|
|
|
|
|
line_cfg = gpiod_line_config_new();
|
|
|
|
|
if (!line_cfg)
|
|
|
|
|
goto free_settings;
|
|
|
|
|
|
|
|
|
|
ret = gpiod_line_config_add_line_settings(line_cfg, &line, 1, settings);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto free_line_config;
|
|
|
|
|
|
|
|
|
|
req_cfg = gpiod_request_config_new();
|
|
|
|
|
if (!req_cfg)
|
|
|
|
|
goto free_line_config;
|
|
|
|
|
|
|
|
|
|
gpiod_request_config_set_consumer(req_cfg, "eg25-manager");
|
|
|
|
|
|
|
|
|
|
request = gpiod_chip_request_lines(manager->gpiochip[chip], req_cfg, line_cfg);
|
|
|
|
|
|
|
|
|
|
gpiod_request_config_free(req_cfg);
|
|
|
|
|
|
|
|
|
|
free_line_config:
|
|
|
|
|
gpiod_line_config_free(line_cfg);
|
|
|
|
|
|
|
|
|
|
free_settings:
|
|
|
|
|
gpiod_line_settings_free(settings);
|
|
|
|
|
|
|
|
|
|
return request;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gpio_chip_dir_filter(const struct dirent *entry)
|
|
|
|
|
{
|
|
|
|
|
struct gpiod_line *gpio_line;
|
|
|
|
|
struct stat sb;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
|
|
gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line);
|
|
|
|
|
if (!gpio_line)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (asprintf(&path, "/dev/%s", entry->d_name) < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (gpiod_line_request_output(gpio_line, "eg25manager", 0) < 0) {
|
|
|
|
|
gpiod_line_release(gpio_line);
|
|
|
|
|
return NULL;
|
|
|
|
|
if ((lstat(path, &sb) == 0) && (!S_ISLNK(sb.st_mode)) &&
|
|
|
|
|
gpiod_is_gpiochip_device(path))
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
|
|
free(path);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gpio_line;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct gpiod_line *gpio_get_input_line(struct EG25Manager *manager, int chip, int line)
|
|
|
|
|
int gpio_all_chip_paths(char ***paths_ptr)
|
|
|
|
|
{
|
|
|
|
|
struct gpiod_line *gpio_line;
|
|
|
|
|
int i, j, num_chips, ret = 0;
|
|
|
|
|
struct dirent **entries;
|
|
|
|
|
char **paths;
|
|
|
|
|
|
|
|
|
|
gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line);
|
|
|
|
|
if (!gpio_line)
|
|
|
|
|
return NULL;
|
|
|
|
|
num_chips = scandir("/dev/", &entries, gpio_chip_dir_filter, alphasort);
|
|
|
|
|
if (num_chips < 0)
|
|
|
|
|
g_error("gpio: unable to scan /dev: %s", g_strerror(errno));
|
|
|
|
|
|
|
|
|
|
paths = calloc(num_chips, sizeof(*paths));
|
|
|
|
|
if (paths == NULL)
|
|
|
|
|
g_error("gpio: out of memory");
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_chips; i++) {
|
|
|
|
|
if (asprintf(&paths[i], "/dev/%s", entries[i]->d_name) < 0) {
|
|
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
|
free(paths[j]);
|
|
|
|
|
|
|
|
|
|
free(paths);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*paths_ptr = paths;
|
|
|
|
|
ret = num_chips;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_chips; i++)
|
|
|
|
|
free(entries[i]);
|
|
|
|
|
|
|
|
|
|
free(entries);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct gpiod_chip *gpio_chip_open_by_label(const char *label)
|
|
|
|
|
{
|
|
|
|
|
int num_chips, i;
|
|
|
|
|
char **paths;
|
|
|
|
|
const char *clabel;
|
|
|
|
|
struct gpiod_chip *chip;
|
|
|
|
|
struct gpiod_chip_info *cinfo;
|
|
|
|
|
|
|
|
|
|
num_chips = gpio_all_chip_paths(&paths);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_chips; i++) {
|
|
|
|
|
chip = gpiod_chip_open(paths[i]);
|
|
|
|
|
if (!chip)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
cinfo = gpiod_chip_get_info(chip);
|
|
|
|
|
if (!cinfo)
|
|
|
|
|
goto clean_chip_open;
|
|
|
|
|
|
|
|
|
|
clabel = gpiod_chip_info_get_label(cinfo);
|
|
|
|
|
|
|
|
|
|
if (strcmp(label, clabel) == 0) {
|
|
|
|
|
return chip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clean_chip_open:
|
|
|
|
|
gpiod_chip_close(chip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gpiod_line_request_input(gpio_line, "eg25manager") < 0) {
|
|
|
|
|
gpiod_line_release(gpio_line);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gpio_line;
|
|
|
|
|
unsigned int gpio_chip_num_lines(struct EG25Manager *manager, unsigned int chip_num) {
|
|
|
|
|
struct gpiod_chip *chip = manager->gpiochip[chip_num];
|
|
|
|
|
struct gpiod_chip_info *info;
|
|
|
|
|
unsigned int num_lines;
|
|
|
|
|
|
|
|
|
|
info = gpiod_chip_get_info(chip);
|
|
|
|
|
if (!info)
|
|
|
|
|
g_error("gpio: failed to read info: %s", strerror(errno));
|
|
|
|
|
|
|
|
|
|
num_lines = gpiod_chip_info_get_num_lines(info);
|
|
|
|
|
|
|
|
|
|
gpiod_chip_info_free(info);
|
|
|
|
|
|
|
|
|
|
return num_lines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
@@ -169,7 +309,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
toml_datum_t data = toml_string_at(chipslist, i);
|
|
|
|
|
if (!data.ok)
|
|
|
|
|
continue;
|
|
|
|
|
manager->gpiochip[i] = gpiod_chip_open_by_label(data.u.s);
|
|
|
|
|
manager->gpiochip[i] = gpio_chip_open_by_label(data.u.s);
|
|
|
|
|
if (!manager->gpiochip[i])
|
|
|
|
|
g_error("Unable to find GPIO chip '%s'", data.u.s);
|
|
|
|
|
}
|
|
|
|
@@ -185,10 +325,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]);
|
|
|
|
|
|
|
|
|
|
line = toml_int_in(table, "line");
|
|
|
|
|
if (!line.ok || line.u.i < 0 || line.u.i > gpiod_chip_num_lines(manager->gpiochip[chip.u.i]))
|
|
|
|
|
if (!line.ok || line.u.i < 0 || line.u.i > gpio_chip_num_lines(manager, chip.u.i))
|
|
|
|
|
g_error("Wrong line ID for output GPIO '%s'", gpio_out_names[i]);
|
|
|
|
|
|
|
|
|
|
manager->gpio_out[i] = gpio_get_output_line(manager, chip.u.i, line.u.i);
|
|
|
|
|
manager->gpio_out[i] = gpio_request_line(manager, chip.u.i, line.u.i, GPIOD_LINE_DIRECTION_OUTPUT);
|
|
|
|
|
if (!manager->gpio_out[i])
|
|
|
|
|
g_error("Unable to get output GPIO %d", i);
|
|
|
|
|
}
|
|
|
|
@@ -209,10 +349,10 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]);
|
|
|
|
|
|
|
|
|
|
line = toml_int_in(table, "line");
|
|
|
|
|
if (!line.ok || line.u.i < 0 || line.u.i > gpiod_chip_num_lines(manager->gpiochip[chip.u.i]))
|
|
|
|
|
if (!line.ok || line.u.i < 0 || line.u.i > gpio_chip_num_lines(manager, chip.u.i))
|
|
|
|
|
g_error("Wrong line ID for input GPIO '%s'", gpio_in_names[i]);
|
|
|
|
|
|
|
|
|
|
manager->gpio_in[i] = gpio_get_input_line(manager, chip.u.i, line.u.i);
|
|
|
|
|
manager->gpio_in[i] = gpio_request_line(manager, chip.u.i, line.u.i, GPIOD_LINE_DIRECTION_INPUT);
|
|
|
|
|
if (!manager->gpio_in[i])
|
|
|
|
|
g_error("Unable to get input GPIO %d", i);
|
|
|
|
|
}
|
|
|
|
@@ -220,11 +360,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
guint offset, chipidx, gpio_idx;
|
|
|
|
|
|
|
|
|
|
/* Legacy config file, only used on the OG PinePhone */
|
|
|
|
|
manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL);
|
|
|
|
|
manager->gpiochip[0] = gpio_chip_open_by_label(GPIO_CHIP1_LABEL);
|
|
|
|
|
if (!manager->gpiochip[0])
|
|
|
|
|
g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL);
|
|
|
|
|
|
|
|
|
|
manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL);
|
|
|
|
|
manager->gpiochip[1] = gpio_chip_open_by_label(GPIO_CHIP2_LABEL);
|
|
|
|
|
if (!manager->gpiochip[1])
|
|
|
|
|
g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL);
|
|
|
|
|
|
|
|
|
@@ -240,7 +380,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
chipidx = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
manager->gpio_out[i] = gpio_get_input_line(manager, chipidx, offset);
|
|
|
|
|
manager->gpio_out[i] = gpio_request_line(manager, chipidx, offset, GPIOD_LINE_DIRECTION_OUTPUT);
|
|
|
|
|
if (!manager->gpio_out[i])
|
|
|
|
|
g_error("Unable to get output GPIO %d", i);
|
|
|
|
|
}
|
|
|
|
@@ -257,7 +397,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
chipidx = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
manager->gpio_in[i] = gpio_get_input_line(manager, chipidx, offset);
|
|
|
|
|
manager->gpio_in[i] = gpio_request_line(manager, chipidx, offset, GPIOD_LINE_DIRECTION_INPUT);
|
|
|
|
|
if (!manager->gpio_in[i])
|
|
|
|
|
g_error("Unable to get input GPIO %d", i);
|
|
|
|
|
}
|
|
|
|
@@ -269,11 +409,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[])
|
|
|
|
|
gboolean gpio_check_poweroff(struct EG25Manager *manager, gboolean keep_down)
|
|
|
|
|
{
|
|
|
|
|
if (manager->gpio_in[GPIO_IN_STATUS] &&
|
|
|
|
|
gpiod_line_get_value(manager->gpio_in[GPIO_IN_STATUS]) == 1) {
|
|
|
|
|
gpio_line_get_value(manager, GPIO_IN_STATUS) == GPIOD_LINE_VALUE_ACTIVE) {
|
|
|
|
|
|
|
|
|
|
if (keep_down && manager->gpio_out[GPIO_OUT_RESET]) {
|
|
|
|
|
// Asserting RESET line to prevent modem from rebooting
|
|
|
|
|
gpiod_line_set_value(manager->gpio_out[GPIO_OUT_RESET], 1);
|
|
|
|
|
gpio_line_set_value(manager, GPIO_OUT_RESET, GPIOD_LINE_VALUE_ACTIVE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
@@ -288,12 +428,12 @@ void gpio_destroy(struct EG25Manager *manager)
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < GPIO_OUT_COUNT; i++) {
|
|
|
|
|
if (manager->gpio_out[i])
|
|
|
|
|
gpiod_line_release(manager->gpio_out[i]);
|
|
|
|
|
gpiod_line_request_release(manager->gpio_out[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < GPIO_IN_COUNT; i++) {
|
|
|
|
|
if (manager->gpio_in[i])
|
|
|
|
|
gpiod_line_release(manager->gpio_in[i]);
|
|
|
|
|
gpiod_line_request_release(manager->gpio_in[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (manager->gpiochip[0])
|
|
|
|
|