/* * Copyright (C) 2020 Arnaud Ferraris * * SPDX-License-Identifier: GPL-3.0-or-later */ #include "config.h" #include "gpio.h" #include #define GPIO_CHIP1_LABEL "1c20800.pinctrl" #define GPIO_CHIP2_LABEL "1f02c00.pinctrl" #define MAX_GPIOCHIP_LINES 352 enum { GPIO_OUT_DTR = 0, GPIO_OUT_PWRKEY, GPIO_OUT_RESET, GPIO_OUT_APREADY, GPIO_OUT_DISABLE, GPIO_OUT_COUNT }; enum { GPIO_IN_STATUS = 0, GPIO_IN_COUNT }; static char *gpio_out_names[] = { "dtr", "pwrkey", "reset", "apready", "disable", }; static char *gpio_in_names[] = { "status", }; int gpio_sequence_poweron(struct EG25Manager *manager) { gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 1); sleep(1); gpiod_line_set_value(manager->gpio_out[GPIO_OUT_PWRKEY], 0); g_message("Executed power-on/off sequence"); return 0; } int gpio_sequence_shutdown(struct EG25Manager *manager) { gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DISABLE], 1); gpio_sequence_poweron(manager); g_message("Executed power-off sequence"); return 0; } int gpio_sequence_suspend(struct EG25Manager *manager) { gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 1); g_message("Executed suspend sequence"); return 0; } int gpio_sequence_resume(struct EG25Manager *manager) { gpiod_line_set_value(manager->gpio_out[GPIO_OUT_APREADY], 0); g_message("Executed resume sequence"); return 0; } 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); /* Give the modem 200ms to wake from soft sleep */ usleep(200000); g_message("Executed soft wake sequence"); } return 0; } int gpio_sequence_sleep(struct EG25Manager *manager) { gpiod_line_set_value(manager->gpio_out[GPIO_OUT_DTR], 1); g_message("Executed soft sleep sequence"); return 0; } int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) { int i, ret; guint offset, chipidx, gpio_idx; toml_table_t *gpio_config[EG25_CONFIG_COUNT]; for (i = 0; i < EG25_CONFIG_COUNT; i++) gpio_config[i] = config[i] ? toml_table_in(config[i], "gpio") : NULL; if (!gpio_config[EG25_CONFIG_SYS]) g_error("Default config file lacks the 'gpio' section!"); manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL); if (!manager->gpiochip[0]) { g_critical("Unable to open GPIO chip " GPIO_CHIP1_LABEL); return 1; } manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL); if (!manager->gpiochip[1]) { g_critical("Unable to open GPIO chip " GPIO_CHIP2_LABEL); return 1; } for (i = 0; i < GPIO_OUT_COUNT; i++) { if (!config_get_uint(gpio_config, gpio_out_names[i], &gpio_idx)) g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]); if (gpio_idx < MAX_GPIOCHIP_LINES) { offset = gpio_idx; chipidx = 0; } else { offset = gpio_idx - MAX_GPIOCHIP_LINES; chipidx = 1; } manager->gpio_out[i] = gpiod_chip_get_line(manager->gpiochip[chipidx], offset); if (!manager->gpio_out[i]) { g_error("Unable to get output GPIO %d", i); return 1; } ret = gpiod_line_request_output(manager->gpio_out[i], "eg25manager", 0); if (ret < 0) { g_error("Unable to request output GPIO %d", i); return 1; } } for (i = 0; i < GPIO_IN_COUNT; i++) { if (!config_get_uint(gpio_config, gpio_in_names[i], &gpio_idx)) continue; if (gpio_idx < MAX_GPIOCHIP_LINES) { offset = gpio_idx; chipidx = 0; } else { offset = gpio_idx - MAX_GPIOCHIP_LINES; chipidx = 1; } manager->gpio_in[i] = gpiod_chip_get_line(manager->gpiochip[chipidx], offset); if (!manager->gpio_in[i]) { g_warning("Unable to get input GPIO %d", i); continue; } ret = gpiod_line_request_input(manager->gpio_in[i], "eg25manager"); if (ret < 0) { g_warning("Unable to request input GPIO %d", i); manager->gpio_in[i] = NULL; } } return 0; } 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) { 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); } return TRUE; } return FALSE; } void gpio_destroy(struct EG25Manager *manager) { int i; for (i = 0; i < GPIO_OUT_COUNT; i++) { if (manager->gpio_out[i]) gpiod_line_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]); } if (manager->gpiochip[0]) gpiod_chip_close(manager->gpiochip[0]); if (manager->gpiochip[1]) gpiod_chip_close(manager->gpiochip[1]); }