From abf60b793a70f202f8fbefea59d82577ca575078 Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris Date: Wed, 24 Nov 2021 01:17:57 +0100 Subject: [PATCH] gpio: make more generic Instead of assuming we're running on the PinePhone (and therefore hardcoding gpiochip labels and line numbers), make all of those configurable. Legacy config files will still be parsed as long as they lack the `chips` key. Existing config files are also updated to match the new config format. --- data/pine64,pinephone-1.0.toml | 11 +- data/pine64,pinephone-1.1.toml | 11 +- data/pine64,pinephone-1.2.toml | 13 +-- src/gpio.c | 181 ++++++++++++++++++++++++--------- 4 files changed, 152 insertions(+), 64 deletions(-) diff --git a/data/pine64,pinephone-1.0.toml b/data/pine64,pinephone-1.0.toml index 8b91a88..10dbc1e 100644 --- a/data/pine64,pinephone-1.0.toml +++ b/data/pine64,pinephone-1.0.toml @@ -13,11 +13,12 @@ poweron_delay = 100000 #recovery_timeout = 9 [gpio] -dtr = 358 -pwrkey = 35 -reset = 68 -apready = 231 -disable = 232 +chips = [ "1c20800.pinctrl", "1f02c00.pinctrl" ] +dtr = { chip = 1, line = 6 } +pwrkey = { chip = 0, line = 35 } +reset = { chip = 0, line = 68 } +apready = { chip = 0, line = 231 } +disable = { chip = 0, line = 232 } [at] uart = "/dev/ttyS2" diff --git a/data/pine64,pinephone-1.1.toml b/data/pine64,pinephone-1.1.toml index fd09462..9242a91 100644 --- a/data/pine64,pinephone-1.1.toml +++ b/data/pine64,pinephone-1.1.toml @@ -13,11 +13,12 @@ poweron_delay = 100000 #recovery_timeout = 9 [gpio] -dtr = 358 -pwrkey = 35 -reset = 68 -apready = 231 -disable = 232 +chips = [ "1c20800.pinctrl", "1f02c00.pinctrl" ] +dtr = { chip = 1, line = 6 } +pwrkey = { chip = 0, line = 35 } +reset = { chip = 0, line = 68 } +apready = { chip = 0, line = 231 } +disable = { chip = 0, line = 232 } [at] uart = "/dev/ttyS2" diff --git a/data/pine64,pinephone-1.2.toml b/data/pine64,pinephone-1.2.toml index 4244bee..3754274 100644 --- a/data/pine64,pinephone-1.2.toml +++ b/data/pine64,pinephone-1.2.toml @@ -9,12 +9,13 @@ poweron_delay = 100000 #recovery_timeout = 9 [gpio] -dtr = 34 -pwrkey = 35 -reset = 68 -apready = 231 -disable = 232 -status = 233 +chips = [ "1c20800.pinctrl" ] +dtr = { chip = 0, line = 34 } +pwrkey = { chip = 0, line = 35 } +reset = { chip = 0, line = 68 } +apready = { chip = 0, line = 231 } +disable = { chip = 0, line = 232 } +status = { chip = 0, line = 233 } [at] uart = "/dev/ttyS2" diff --git a/src/gpio.c b/src/gpio.c index c7cfc53..f9e707c 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -9,9 +9,9 @@ #include +/* Those defines are used for legacy config files only */ #define GPIO_CHIP1_LABEL "1c20800.pinctrl" #define GPIO_CHIP2_LABEL "1f02c00.pinctrl" - #define MAX_GPIOCHIP_LINES 352 enum { @@ -101,10 +101,41 @@ int gpio_sequence_sleep(struct EG25Manager *manager) return 0; } +struct gpiod_line *gpio_get_output_line(struct EG25Manager *manager, int chip, int line) +{ + struct gpiod_line *gpio_line; + + gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line); + if (!gpio_line) + return NULL; + + if (gpiod_line_request_output(gpio_line, "eg25manager", 0) < 0) { + gpiod_line_release(gpio_line); + return NULL; + } + + return gpio_line; +} + +struct gpiod_line *gpio_get_input_line(struct EG25Manager *manager, int chip, int line) +{ + struct gpiod_line *gpio_line; + + gpio_line = gpiod_chip_get_line(manager->gpiochip[chip], line); + if (!gpio_line) + return NULL; + + if (gpiod_line_request_input(gpio_line, "eg25manager") < 0) { + gpiod_line_release(gpio_line); + return NULL; + } + + return gpio_line; +} + int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) { - int i, ret; - guint offset, chipidx, gpio_idx; + int i; toml_table_t *gpio_config[EG25_CONFIG_COUNT]; for (i = 0; i < EG25_CONFIG_COUNT; i++) @@ -113,65 +144,119 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) 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; - } + /* + * The system config could have the `chips` key, but the user one + * could still use the old format! In order to avoid problems, we + * should use the new format only if: + * - there's no user config file + or + * - the user config file contains the `chips` key + * Otherwise we might start parsing the system config with the new + * format, but error out if user config overrides gpios using the + * old format + */ + if (!gpio_config[EG25_CONFIG_USER] || toml_array_in(gpio_config[EG25_CONFIG_USER], "chips")) + { + int numchips; + toml_array_t *chipslist = NULL; - 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; - } + config_get_array(gpio_config, "chips", &chipslist); + numchips = toml_array_nelem(chipslist); + if (numchips > 2) + g_error("Requesting too many GPIO chips!"); - 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; + for (i = 0; i < numchips; i++) { + 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->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); + for (i = 0; i < GPIO_OUT_COUNT; i++) { + toml_table_t *table; + toml_datum_t chip, line; + if (!config_get_table(gpio_config, gpio_out_names[i], &table)) + g_error("Unable to get config for output GPIO '%s'", gpio_out_names[i]); + + chip = toml_int_in(table, "chip"); + if (!chip.ok || chip.u.i < 0 || chip.u.i > 2) + 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])) + g_error("Wrong chip ID for output GPIO '%s'", gpio_out_names[i]); + + manager->gpio_out[i] = gpio_get_output_line(manager, chip.u.i, line.u.i); + if (!manager->gpio_out[i]) + g_error("Unable to get output GPIO %d", i); + } + + for (i = 0; i < GPIO_IN_COUNT; i++) { + toml_table_t *table; + toml_datum_t chip, line; + if (!config_get_table(gpio_config, gpio_in_names[i], &table)) + g_error("Unable to get config for input GPIO '%s'", gpio_in_names[i]); + + chip = toml_int_in(table, "chip"); + if (!chip.ok || chip.u.i < 0 || chip.u.i > 2) + 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])) + g_error("Wrong chip ID for input GPIO '%s'", gpio_in_names[i]); + + manager->gpio_in[i] = gpio_get_input_line(manager, chip.u.i, line.u.i); + if (!manager->gpio_in[i]) + g_error("Unable to get input GPIO %d", i); + } + } else { + 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); + if (!manager->gpiochip[0]) { + g_critical("Unable to open GPIO chip " GPIO_CHIP1_LABEL); 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); + 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_IN_COUNT; i++) { - if (!config_get_uint(gpio_config, gpio_in_names[i], &gpio_idx)) - continue; + 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; + if (gpio_idx < MAX_GPIOCHIP_LINES) { + offset = gpio_idx; + chipidx = 0; + } else { + offset = gpio_idx - MAX_GPIOCHIP_LINES; + chipidx = 1; + } + + manager->gpio_out[i] = gpio_get_input_line(manager, chipidx, offset); + if (!manager->gpio_out[i]) + g_error("Unable to get output GPIO %d", i); } - 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; - } + for (i = 0; i < GPIO_IN_COUNT; i++) { + if (!config_get_uint(gpio_config, gpio_in_names[i], &gpio_idx)) + 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; + if (gpio_idx < MAX_GPIOCHIP_LINES) { + offset = gpio_idx; + chipidx = 0; + } else { + offset = gpio_idx - MAX_GPIOCHIP_LINES; + chipidx = 1; + } + + manager->gpio_in[i] = gpio_get_input_line(manager, chipidx, offset); + if (!manager->gpio_in[i]) + g_error("Unable to get input GPIO %d", i); } }