From 652e33821309c83ee214ff196c9c0aee79594d00 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 1 Sep 2024 14:34:01 +0300 Subject: [PATCH 01/11] atx_psu: poll power-good while ACTIVE to warn on power loss --- components/atx_psu/atx_psu.c | 60 ++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index 196d2342..d4a5ac20 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -15,7 +15,11 @@ #define ATX_PSU_STATUS_POWER_ENABLE_BIT (1 << 0) #define ATX_PSU_STATUS_POWER_GOOD_BIT (1 << 1) -#define ATX_PSU_POWER_GOOD_TICKS (100 / portTICK_PERIOD_MS) +// poll for PG while ACTIVATE -> ACTIVE +#define ATX_PSU_POWER_GOOD_ACTIVATE_TICKS (100 / portTICK_PERIOD_MS) + +// poll for PG while ACTIVATE +#define ATX_PSU_POWER_GOOD_ACTIVE_TICKS (1000 / portTICK_PERIOD_MS) enum atx_psu_state { INACTIVE, @@ -148,11 +152,12 @@ void atx_psu_main(void *arg) // indefinite wait for any bit if ((bits = atx_psu_wait(atx_psu, portMAX_DELAY))) { - LOG_DEBUG("INACTIVE -> ACTIVE<%08x>", bits); + LOG_DEBUG("INACTIVE -> ACTIVATE<%08x>", bits); set_power_enable(atx_psu); state = ACTIVATE; + LOG_INFO("power-on"); } else { - LOG_DEBUG("INACTIVE -> ACTIVATE"); + LOG_DEBUG("INACTIVE"); } break; @@ -160,16 +165,18 @@ void atx_psu_main(void *arg) case ACTIVATE: LOG_DEBUG("ACTIVATE..."); - // indefinite wait for clear bit - if (get_power_good(atx_psu)) { - LOG_DEBUG("ACTIVATE -> ACTIVE"); - state = ACTIVE; - } else if ((bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_TICKS))) { - // test power_good - } else { - LOG_DEBUG("ACTIVE -> DEACTIVATE"); + // poll for PG + if (!(bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_ACTIVATE_TICKS))) { + LOG_DEBUG("ACTIVATE -> DEACTIVATE"); // delay deactivation state = DEACTIVATE; + } else if (get_power_good(atx_psu)) { + LOG_DEBUG("ACTIVATE -> ACTIVE<%08x>", bits); + state = ACTIVE; + LOG_INFO("power-good"); + } else { + // re-ACTIVATE to test power_good + LOG_DEBUG("ACTIVATE"); } break; @@ -177,13 +184,18 @@ void atx_psu_main(void *arg) case ACTIVE: LOG_DEBUG("ACTIVE..."); - // indefinite wait for clear bit - if ((bits = atx_psu_wait_clear(atx_psu, portMAX_DELAY))) { - LOG_DEBUG("ACTIVE -> ACTIVE<%08x>", bits); - } else { + // wait for clear bit, poll for PG + if (!(bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_ACTIVE_TICKS))) { LOG_DEBUG("ACTIVE -> DEACTIVATE"); // delay deactivation state = DEACTIVATE; + } else if (get_power_good(atx_psu)) { + LOG_DEBUG("ACTIVE<%08x>", bits); + } else { + // re-ACTIVATE to test power_good + LOG_DEBUG("ACTIVE -> ACTIVATE"); + state = ACTIVATE; + LOG_WARN("lost power-good!"); } break; @@ -192,13 +204,17 @@ void atx_psu_main(void *arg) LOG_DEBUG("DEACTIVATE..."); // wait for any bit or deactivate on timeout - if ((bits = atx_psu_wait(atx_psu, atx_psu->options.timeout))) { - LOG_DEBUG("DEACTIVATE -> ACTIVE<%08x>", bits); - state = ACTIVE; - } else { + if (!(bits = atx_psu_wait(atx_psu, atx_psu->options.timeout))) { LOG_DEBUG("DEACTIVATE -> INACTIVE"); clear_power_enable(atx_psu); state = INACTIVE; + LOG_INFO("power-off"); + } else if (get_power_good(atx_psu)) { + LOG_DEBUG("DEACTIVATE -> ACTIVE<%08x>", bits); + state = ACTIVE; + } else { + LOG_DEBUG("DEACTIVATE -> ACTIVATE<%08x>", bits); + state = ACTIVATE; } break; @@ -211,6 +227,8 @@ void atx_psu_main(void *arg) void atx_psu_power_enable(struct atx_psu *atx_psu, enum atx_psu_bit bit) { + LOG_DEBUG("bit=%08x", ATX_PSU_STATE_BIT(bit)); + xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); } @@ -227,7 +245,7 @@ int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t } else { bits = xEventGroupWaitBits(atx_psu->status, ATX_PSU_STATUS_POWER_ENABLE_BIT | ATX_PSU_STATUS_POWER_GOOD_BIT, pdFALSE, pdTRUE, timeout); - LOG_DEBUG("bits=%08x", bits); + //LOG_DEBUG("bits=%08x", bits); return (bits & ATX_PSU_STATUS_POWER_GOOD_BIT); } @@ -235,6 +253,8 @@ int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t void atx_psu_standby(struct atx_psu *atx_psu, enum atx_psu_bit bit) { + LOG_DEBUG("bit=%08x", ATX_PSU_STATE_BIT(bit)); + xEventGroupClearBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_CLEAR_BIT); } From 2d3325f6583296cea7c9de98eeb1c15c44a3c24c Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 1 Sep 2024 14:43:29 +0300 Subject: [PATCH 02/11] main atx_psu: alert on power_good timeout --- main/atx_psu.c | 4 +++- main/user.c | 5 +++++ main/user.h | 1 + main/user_leds.c | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/main/atx_psu.c b/main/atx_psu.c index c5da2185..4ccbb003 100644 --- a/main/atx_psu.c +++ b/main/atx_psu.c @@ -2,6 +2,7 @@ #include "atx_psu_config.h" #include "atx_psu_gpio.h" #include "atx_psu_state.h" +#include "user.h" #include "tasks.h" #include @@ -79,7 +80,8 @@ int wait_atx_psu_bit(enum atx_psu_bit bit, TickType_t timeout) } else if (atx_psu_power_good(atx_psu, bit, timeout)) { return 0; } else { - LOG_WARN("power_good timeout=%d expired, is ATX-PSU connected?", timeout); + LOG_WARN("power_good timeout=%dms expired, is ATX-PSU connected?", timeout * portTICK_PERIOD_MS); + user_alert(USER_ALERT_ERROR_ATX_PSU_TIMEOUT); // TODO: clear alert? return -1; } } diff --git a/main/user.c b/main/user.c index 8a4223b1..a0709d83 100644 --- a/main/user.c +++ b/main/user.c @@ -43,6 +43,11 @@ static const char *user_alert_str(enum user_alert alert) case USER_ALERT_ERROR_WIFI: return "ERROR_WIFI"; case USER_ALERT_ERROR_START: return "ERROR_START"; case USER_ALERT_ERROR_DMX: return "ERROR_DMX"; + + case USER_ALERT_ERROR_LEDS_SEQUENCE: return "ERROR_LEDS_SEQUENCE"; + case USER_ALERT_ERROR_LEDS_SEQUENCE_READ: return "ERROR_LEDS_SEQUENCE_READ"; + case USER_ALERT_ERROR_ATX_PSU_TIMEOUT: return "ERROR_ATX_PSU_TIMEOUT"; + default: return NULL; } } diff --git a/main/user.h b/main/user.h index f567755b..8d800503 100644 --- a/main/user.h +++ b/main/user.h @@ -30,6 +30,7 @@ enum user_alert { USER_ALERT_ERROR_DMX, USER_ALERT_ERROR_LEDS_SEQUENCE, USER_ALERT_ERROR_LEDS_SEQUENCE_READ, + USER_ALERT_ERROR_ATX_PSU_TIMEOUT, USER_ALERT_MAX }; diff --git a/main/user_leds.c b/main/user_leds.c index 6d26bd46..8266d255 100644 --- a/main/user_leds.c +++ b/main/user_leds.c @@ -32,6 +32,7 @@ enum user_leds_state user_alert_led_state[USER_ALERT_MAX] = { [USER_ALERT_ERROR_DMX] = USER_LEDS_ON, [USER_ALERT_ERROR_LEDS_SEQUENCE] = USER_LEDS_ON, [USER_ALERT_ERROR_LEDS_SEQUENCE_READ] = USER_LEDS_FLASH, + [USER_ALERT_ERROR_ATX_PSU_TIMEOUT] = USER_LEDS_ON, }; // state From 111637eefdc985b8cdc27bf61fb95cdc13c37b35 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 1 Sep 2024 15:21:16 +0300 Subject: [PATCH 03/11] gpio: refactor host vs i2c intr pins --- components/gpio/esp32/gpio_host.c | 2 +- components/gpio/esp32/gpio_intr.c | 64 +++++++++++++++++++++++++------ components/gpio/gpio.h | 5 ++- components/gpio/i2c.c | 15 ++++---- 4 files changed, 63 insertions(+), 23 deletions(-) diff --git a/components/gpio/esp32/gpio_host.c b/components/gpio/esp32/gpio_host.c index 7b71ffa2..97e78e54 100644 --- a/components/gpio/esp32/gpio_host.c +++ b/components/gpio/esp32/gpio_host.c @@ -145,7 +145,7 @@ int gpio_host_setup(const struct gpio_options *options) gpio_host_setup_rtc(gpio, inverted, !inverted); } if (interrupt) { - gpio_intr_setup_pin(options, gpio); + gpio_intr_setup_host_pin(gpio, options); } } diff --git a/components/gpio/esp32/gpio_intr.c b/components/gpio/esp32/gpio_intr.c index 5573ec09..2daa5ffc 100644 --- a/components/gpio/esp32/gpio_intr.c +++ b/components/gpio/esp32/gpio_intr.c @@ -11,7 +11,18 @@ intr_handle_t gpio_intr_handle; -const struct gpio_options *gpio_intr_options[GPIO_HOST_PIN_COUNT] = {}; +struct gpio_intr_options { + enum gpio_intr_type { + GPIO_INTR_TYPE_NONE = 0, + GPIO_INTR_TYPE_HOST, + GPIO_INTR_TYPE_I2C, + } type; + + union { + const struct gpio_options *host; + const struct gpio_i2c_dev *i2c_dev; + }; +} gpio_intr_options[GPIO_HOST_PIN_COUNT] = {}; static IRAM_ATTR void gpio_isr (void *arg) { @@ -21,16 +32,21 @@ static IRAM_ATTR void gpio_isr (void *arg) LOG_ISR_DEBUG("core=%d pins=" GPIO_PINS_FMT, core, GPIO_PINS_ARGS(pins)); for (gpio_pin_t gpio = 0; gpio < GPIO_HOST_PIN_COUNT; gpio++) { - const struct gpio_options *options; + if ((pins & GPIO_PINS(gpio))) { + const struct gpio_intr_options *options = &gpio_intr_options[gpio]; - if ((pins & GPIO_PINS(gpio)) && (options = gpio_intr_options[gpio])) { switch(options->type) { - case GPIO_TYPE_HOST: - gpio_host_intr_handler(options, pins); + case GPIO_INTR_TYPE_NONE: break; - case GPIO_TYPE_I2C: - gpio_i2c_intr_handler(options, pins); + case GPIO_INTR_TYPE_HOST: + LOG_ISR_DEBUG("pin=%d host options=%p", gpio, options->host); + gpio_host_intr_handler(options->host, pins); + break; + + case GPIO_INTR_TYPE_I2C: + LOG_ISR_DEBUG("pin=%d i2c dev=%p", gpio, options->i2c_dev); + gpio_i2c_intr_handler(options->i2c_dev, pins); break; } } @@ -53,16 +69,40 @@ int gpio_intr_init() return 0; } -void gpio_intr_setup_pin(const struct gpio_options *options, gpio_pin_t gpio) +void gpio_intr_setup_host_pin(gpio_pin_t gpio, const struct gpio_options *options) { if (gpio >= GPIO_HOST_PIN_COUNT) { - LOG_WARN("[%d] -> %p: invalid gpio_pin_t", gpio, options); - return; + LOG_FATAL("[%d] invalid gpio_pin_t", gpio); + } + + if (gpio_intr_options[gpio].type) { + LOG_WARN("[%d] intr pin conflict type=%u", gpio, gpio_intr_options[gpio].type); + } + + LOG_DEBUG("[%d] host options=%p", gpio, options); + + gpio_intr_options[gpio] = (struct gpio_intr_options) { + .type = GPIO_INTR_TYPE_HOST, + .host = options, + }; +} + +void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, const struct gpio_i2c_dev *i2c_dev) +{ + if (gpio >= GPIO_HOST_PIN_COUNT) { + LOG_FATAL("[%d] invalid gpio_pin_t", gpio); + } + + if (gpio_intr_options[gpio].type) { + LOG_WARN("[%d] intr pin conflict type=%u", gpio, gpio_intr_options[gpio].type); } - LOG_DEBUG("[%d] -> %p", gpio, options); + LOG_DEBUG("[%d] i2c dev=%p", gpio, i2c_dev); - gpio_intr_options[gpio] = options; + gpio_intr_options[gpio] = (struct gpio_intr_options) { + .type = GPIO_INTR_TYPE_I2C, + .i2c_dev = i2c_dev, + }; } int gpio_intr_core() diff --git a/components/gpio/gpio.h b/components/gpio/gpio.h index 27c59843..204b817d 100644 --- a/components/gpio/gpio.h +++ b/components/gpio/gpio.h @@ -45,7 +45,8 @@ int gpio_host_set_all(const struct gpio_options *options); /* gpio_intr.c */ int gpio_intr_init(); -void gpio_intr_setup_pin(const struct gpio_options *options, gpio_pin_t gpio); +void gpio_intr_setup_host_pin(gpio_pin_t gpio, const struct gpio_options *options); +void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, const struct gpio_i2c_dev *i2c_dev); #if !CONFIG_IDF_TARGET_ESP8266 int gpio_intr_core(); @@ -53,7 +54,7 @@ void gpio_intr_setup_pin(const struct gpio_options *options, gpio_pin_t gpio); #if GPIO_I2C_ENABLED /* i2c.cc */ - void gpio_i2c_intr_handler (const struct gpio_options *options, gpio_pins_t pins); + void gpio_i2c_intr_handler (const struct gpio_i2c_dev *i2c_dev, gpio_pins_t pins); int gpio_i2c_setup(const struct gpio_options *options); int gpio_i2c_setup_input(const struct gpio_options *options, gpio_pins_t pins); diff --git a/components/gpio/i2c.c b/components/gpio/i2c.c index 17b217dd..2d801965 100644 --- a/components/gpio/i2c.c +++ b/components/gpio/i2c.c @@ -6,9 +6,9 @@ #if GPIO_I2C_ENABLED #include - IRAM_ATTR void gpio_i2c_intr_handler (const struct gpio_options *options, gpio_pins_t pins) + IRAM_ATTR void gpio_i2c_intr_handler (const struct gpio_i2c_dev *dev, gpio_pins_t pins) { - struct gpio_i2c_dev *dev = options->i2c_dev; + const struct gpio_options *options; for (unsigned i = 0; i < GPIO_I2C_PINS_MAX; i++) { if (!(options = dev->intr_pins[i])) { @@ -38,6 +38,9 @@ } if (options->int_pin > 0) { + // TODO: multiple gpio_i2c_dev sharing the same int_pin? + gpio_intr_setup_i2c_pin(options->int_pin, dev); + if ((err = gpio_host_setup_intr_pin(options->int_pin, GPIO_INTR_NEGEDGE))) { LOG_ERROR("gpio_host_setup_intr_pin"); return err; @@ -110,12 +113,8 @@ } if (options->interrupt_pins) { - if (options->i2c_dev->options.int_pin > 0) { - // XXX: multiple gpio_i2c_dev sharing the same int_pin? - gpio_intr_setup_pin(options, options->i2c_dev->options.int_pin); - } else { - LOG_ERROR("interrupt_pins without i2c_dev int_pin"); - return -1; + if (!options->i2c_dev->options.int_pin) { + LOG_WARN("interrupt_pins without i2c_dev int_pin"); } for (gpio_pin_t pin = 0; pin < GPIO_I2C_PINS_MAX; pin++) { From 26cb0ea01dc0868f593f49c5868937cd00cc45c2 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 1 Sep 2024 16:21:11 +0300 Subject: [PATCH 04/11] gpio: support for multiple gpio_i2c_dev sharing the same int_pin --- components/gpio/esp32/gpio_intr.c | 25 ++++++++++++++++++------- components/gpio/gpio.h | 4 +++- components/gpio/i2c.c | 1 - components/gpio/include/gpio.h | 2 -- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/components/gpio/esp32/gpio_intr.c b/components/gpio/esp32/gpio_intr.c index 2daa5ffc..2a3fa432 100644 --- a/components/gpio/esp32/gpio_intr.c +++ b/components/gpio/esp32/gpio_intr.c @@ -45,8 +45,10 @@ static IRAM_ATTR void gpio_isr (void *arg) break; case GPIO_INTR_TYPE_I2C: - LOG_ISR_DEBUG("pin=%d i2c dev=%p", gpio, options->i2c_dev); - gpio_i2c_intr_handler(options->i2c_dev, pins); + for (const struct gpio_i2c_dev *i2c_dev = options->i2c_dev; i2c_dev; i2c_dev = i2c_dev->intr_link) { + LOG_ISR_DEBUG("pin=%d i2c dev=%p", gpio, i2c_dev); + gpio_i2c_intr_handler(i2c_dev, pins); + } break; } } @@ -87,17 +89,26 @@ void gpio_intr_setup_host_pin(gpio_pin_t gpio, const struct gpio_options *option }; } -void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, const struct gpio_i2c_dev *i2c_dev) +void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, struct gpio_i2c_dev *i2c_dev) { if (gpio >= GPIO_HOST_PIN_COUNT) { LOG_FATAL("[%d] invalid gpio_pin_t", gpio); } - if (gpio_intr_options[gpio].type) { - LOG_WARN("[%d] intr pin conflict type=%u", gpio, gpio_intr_options[gpio].type); - } + switch (gpio_intr_options[gpio].type) { + case GPIO_INTR_TYPE_NONE: + LOG_DEBUG("[%d] i2c dev=%p", gpio, i2c_dev); + break; - LOG_DEBUG("[%d] i2c dev=%p", gpio, i2c_dev); + case GPIO_INTR_TYPE_HOST: + LOG_WARN("[%d] intr pin conflict type=%u", gpio, gpio_intr_options[gpio].type); + break; + + case GPIO_INTR_TYPE_I2C: + LOG_DEBUG("[%d] i2c dev=%p link=%p", gpio, i2c_dev, gpio_intr_options[gpio].i2c_dev); + i2c_dev->intr_link = gpio_intr_options[gpio].i2c_dev; + break; + } gpio_intr_options[gpio] = (struct gpio_intr_options) { .type = GPIO_INTR_TYPE_I2C, diff --git a/components/gpio/gpio.h b/components/gpio/gpio.h index 204b817d..8239d1c9 100644 --- a/components/gpio/gpio.h +++ b/components/gpio/gpio.h @@ -27,6 +27,8 @@ struct gpio_i2c_options options; SemaphoreHandle_t mutex; union gpio_i2c_state state; + + const struct gpio_i2c_dev *intr_link; // multiple i2c devs sharing the same host intr pin const struct gpio_options *intr_pins[GPIO_I2C_PINS_MAX]; }; #endif @@ -46,7 +48,7 @@ int gpio_host_set_all(const struct gpio_options *options); /* gpio_intr.c */ int gpio_intr_init(); void gpio_intr_setup_host_pin(gpio_pin_t gpio, const struct gpio_options *options); -void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, const struct gpio_i2c_dev *i2c_dev); +void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, struct gpio_i2c_dev *i2c_dev); #if !CONFIG_IDF_TARGET_ESP8266 int gpio_intr_core(); diff --git a/components/gpio/i2c.c b/components/gpio/i2c.c index 2d801965..1cc537ad 100644 --- a/components/gpio/i2c.c +++ b/components/gpio/i2c.c @@ -38,7 +38,6 @@ } if (options->int_pin > 0) { - // TODO: multiple gpio_i2c_dev sharing the same int_pin? gpio_intr_setup_i2c_pin(options->int_pin, dev); if ((err = gpio_host_setup_intr_pin(options->int_pin, GPIO_INTR_NEGEDGE))) { diff --git a/components/gpio/include/gpio.h b/components/gpio/include/gpio.h index f396e773..1ee6e376 100644 --- a/components/gpio/include/gpio.h +++ b/components/gpio/include/gpio.h @@ -147,8 +147,6 @@ enum gpio_type { i2c_port_t port; uint8_t addr; gpio_pin_t int_pin; - - /* XXX: no support for multiple i2c_gpio_dev sharing the same int_pin */ }; struct gpio_i2c_dev; From bd96c36867ae417ccdfceb3c3073d5b12ed38a7a Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 1 Sep 2024 16:22:58 +0300 Subject: [PATCH 05/11] gpio: move i2c timeout to gpio_i2c_options / gpio_i2c_dev --- components/gpio/i2c_pca55xx.c | 39 ++++++++++++++++++++++------------ components/gpio/include/gpio.h | 6 +----- main/Kconfig | 7 +++++- main/atx_psu_gpio.c | 8 +------ main/i2c_gpio.c | 3 +++ main/leds_gpio.c | 6 ------ main/user_leds.c | 11 ++-------- 7 files changed, 38 insertions(+), 42 deletions(-) diff --git a/components/gpio/i2c_pca55xx.c b/components/gpio/i2c_pca55xx.c index 943a419e..07d4f0b2 100644 --- a/components/gpio/i2c_pca55xx.c +++ b/components/gpio/i2c_pca55xx.c @@ -19,12 +19,12 @@ return (pins) & GPIO_I2C_PCA9554_PINS_MASK; } - static int gpio_i2c_pca54xx_write(const struct gpio_i2c_options *options, enum pca55xx_cmd cmd, uint8_t value, TickType_t timeout) + static int gpio_i2c_pca54xx_write(const struct gpio_i2c_options *options, enum pca55xx_cmd cmd, uint8_t value) { uint8_t buf[] = { cmd, value }; esp_err_t err; - if ((err = i2c_master_write_to_device(options->port, GPIO_I2C_PCA9554_ADDR(options->addr), buf, sizeof(buf), timeout))) { + if ((err = i2c_master_write_to_device(options->port, GPIO_I2C_PCA9554_ADDR(options->addr), buf, sizeof(buf), options->timeout))) { LOG_ERROR("i2c_master_write_to_device port=%d addr=%u: %s", options->port, GPIO_I2C_PCA9554_ADDR(options->addr), esp_err_to_name(err)); return -1; } @@ -32,13 +32,13 @@ return 0; } - static int gpio_i2c_pca54xx_read(const struct gpio_i2c_options *options, enum pca55xx_cmd cmd, uint8_t *value, TickType_t timeout) + static int gpio_i2c_pca54xx_read(const struct gpio_i2c_options *options, enum pca55xx_cmd cmd, uint8_t *value) { uint8_t wbuf[] = { cmd }; uint8_t rbuf[1] = { }; esp_err_t err; - if ((err = i2c_master_write_read_device(options->port, GPIO_I2C_PCA9554_ADDR(options->addr), wbuf, sizeof(wbuf), rbuf, sizeof(rbuf), timeout))) { + if ((err = i2c_master_write_read_device(options->port, GPIO_I2C_PCA9554_ADDR(options->addr), wbuf, sizeof(wbuf), rbuf, sizeof(rbuf), options->timeout))) { LOG_ERROR("i2c_master_write_to_device port=%d addr=%u: %s", options->port, GPIO_I2C_PCA9554_ADDR(options->addr), esp_err_to_name(err)); return -1; } @@ -48,12 +48,12 @@ return 0; } - static int gpio_i2c_pca54xx_input(struct gpio_i2c_dev *dev, uint8_t mask, uint8_t *valuep, TickType_t timeout) + static int gpio_i2c_pca54xx_input(const struct gpio_i2c_dev *dev, uint8_t mask, uint8_t *valuep) { uint8_t value; int err; - if ((err = gpio_i2c_pca54xx_read(&dev->options, PCA55XX_CMD_INPUT_PORT, &value, timeout))) { + if ((err = gpio_i2c_pca54xx_read(&dev->options, PCA55XX_CMD_INPUT_PORT, &value))) { return err; } @@ -62,6 +62,7 @@ return 0; } + // XXX: what timeout to use for locked operations on shared gpio_i2c_dev? static int gpio_i2c_pca54xx_output(struct gpio_i2c_dev *dev, uint8_t mask, uint8_t value, TickType_t timeout) { int err = 0; @@ -75,7 +76,7 @@ LOG_DEBUG("dev=%p mask=%02x value=%02x -> output=%02x", dev, mask, value, dev->state.pca54xx.output); - if ((err = gpio_i2c_pca54xx_write(&dev->options, PCA55XX_CMD_OUTPUT_PORT, dev->state.pca54xx.output, timeout))) { + if ((err = gpio_i2c_pca54xx_write(&dev->options, PCA55XX_CMD_OUTPUT_PORT, dev->state.pca54xx.output))) { LOG_ERROR("gpio_i2c_pca54xx_write"); goto error; } @@ -86,6 +87,7 @@ return err; } + // XXX: what timeout to use for locked operations on shared gpio_i2c_dev? static int gpio_i2c_pca54xx_config(struct gpio_i2c_dev *dev, uint8_t mask, uint8_t value, TickType_t timeout) { int err = 0; @@ -99,7 +101,7 @@ LOG_DEBUG("dev=%p mask=%02x value=%02x -> config=%02x", dev, mask, value, dev->state.pca54xx.config); - if ((err = gpio_i2c_pca54xx_write(&dev->options, PCA55XX_CMD_CONFIG_PORT, dev->state.pca54xx.config, timeout))) { + if ((err = gpio_i2c_pca54xx_write(&dev->options, PCA55XX_CMD_CONFIG_PORT, dev->state.pca54xx.config))) { LOG_ERROR("gpio_i2c_pca54xx_write"); goto error; } @@ -121,14 +123,15 @@ int gpio_i2c_pca54xx_setup(const struct gpio_options *options) { + struct gpio_i2c_dev *i2c_dev = options->i2c_dev; int err; - if ((err = gpio_i2c_pca54xx_output(options->i2c_dev, pca55x_pins(options->out_pins), pca55x_pins(0 ^ options->inverted_pins), options->i2c_timeout))) { + if ((err = gpio_i2c_pca54xx_output(i2c_dev, pca55x_pins(options->out_pins), pca55x_pins(0 ^ options->inverted_pins), i2c_dev->options.timeout))) { LOG_ERROR("gpio_i2c_pca54xx_output"); return err; } - if ((err = gpio_i2c_pca54xx_config(options->i2c_dev, pca55x_pins(options->in_pins | options->out_pins), pca55x_pins(~options->out_pins), options->i2c_timeout))) { + if ((err = gpio_i2c_pca54xx_config(i2c_dev, pca55x_pins(options->in_pins | options->out_pins), pca55x_pins(~options->out_pins), i2c_dev->options.timeout))) { LOG_ERROR("gpio_i2c_pca54xx_config"); return err; } @@ -138,15 +141,18 @@ int gpio_i2c_pca54xx_setup_input(const struct gpio_options *options, gpio_pins_t pins) { - return gpio_i2c_pca54xx_config(options->i2c_dev, pca55x_pins(options->in_pins | options->out_pins), pca55x_pins(~(options->out_pins & ~pins)), options->i2c_timeout); + struct gpio_i2c_dev *i2c_dev = options->i2c_dev; + + return gpio_i2c_pca54xx_config(i2c_dev, pca55x_pins(options->in_pins | options->out_pins), pca55x_pins(~(options->out_pins & ~pins)), i2c_dev->options.timeout); } int gpio_i2c_pca54xx_get(const struct gpio_options *options, gpio_pins_t *pins) { + struct gpio_i2c_dev *i2c_dev = options->i2c_dev; uint8_t value; int err; - if ((err = gpio_i2c_pca54xx_input(options->i2c_dev, pca55x_pins(options->in_pins), &value, options->i2c_timeout))) { + if ((err = gpio_i2c_pca54xx_input(i2c_dev, pca55x_pins(options->in_pins), &value))) { return err; } @@ -157,11 +163,16 @@ int gpio_i2c_pca54xx_setup_output(const struct gpio_options *options, gpio_pins_t pins) { - return gpio_i2c_pca54xx_config(options->i2c_dev, pca55x_pins(options->in_pins | options->out_pins), pca55x_pins(~(options->out_pins & pins)), options->i2c_timeout); + struct gpio_i2c_dev *i2c_dev = options->i2c_dev; + + return gpio_i2c_pca54xx_config(i2c_dev, pca55x_pins(options->in_pins | options->out_pins), pca55x_pins(~(options->out_pins & pins)), i2c_dev->options.timeout); } int gpio_i2c_pca54xx_set(const struct gpio_options *options, gpio_pins_t pins) { - return gpio_i2c_pca54xx_output(options->i2c_dev, pca55x_pins(options->out_pins), pca55x_pins(pins ^ options->inverted_pins), options->i2c_timeout); + struct gpio_i2c_dev *i2c_dev = options->i2c_dev; + + return gpio_i2c_pca54xx_output(i2c_dev, pca55x_pins(options->out_pins), pca55x_pins(pins ^ options->inverted_pins), i2c_dev->options.timeout); } + #endif diff --git a/components/gpio/include/gpio.h b/components/gpio/include/gpio.h index 1ee6e376..ba4f4d98 100644 --- a/components/gpio/include/gpio.h +++ b/components/gpio/include/gpio.h @@ -146,6 +146,7 @@ enum gpio_type { enum gpio_i2c_type type; i2c_port_t port; uint8_t addr; + TickType_t timeout; gpio_pin_t int_pin; }; @@ -185,11 +186,6 @@ struct gpio_options { // interrupt handler gpio_interrupt_func_t interrupt_func; void *interrupt_arg; - -#if GPIO_I2C_ENABLED - // for i2c devices - TickType_t i2c_timeout; -#endif }; /* Setup input interrupt handler */ diff --git a/main/Kconfig b/main/Kconfig index 6759717a..a1faf2bb 100644 --- a/main/Kconfig +++ b/main/Kconfig @@ -342,8 +342,13 @@ menu "qmsk-esp-i2c" range 0 15 default 0 + config I2C_GPIO_TIMEOUT + depends on I2C_GPIO_ENABLED + int "Timeout for I2C GPIO expander operations" + default 20 + config I2C_GPIO_INT_PIN - depends on I2C_GPIO_TYPE_PCA9534 || I2C_GPIO_TYPE_PCA9554 + depends on I2C_GPIO_ENABLED int "HOST GPIO pin for I2C bus interrupt, 0 to disable" range 0 39 default 0 diff --git a/main/atx_psu_gpio.c b/main/atx_psu_gpio.c index d4742cc6..0416e9c3 100644 --- a/main/atx_psu_gpio.c +++ b/main/atx_psu_gpio.c @@ -4,10 +4,6 @@ #include -#if GPIO_I2C_ENABLED - #define ATX_PSU_GPIO_I2C_TIMEOUT (20 / portTICK_RATE_MS) -#endif - struct gpio_options atx_psu_gpio_options = { }; @@ -66,13 +62,11 @@ int init_atx_psu_gpio(const struct atx_psu_config *config) case GPIO_TYPE_I2C: i2c_options = gpio_i2c_options(atx_psu_gpio_options.i2c_dev); - atx_psu_gpio_options.i2c_timeout = ATX_PSU_GPIO_I2C_TIMEOUT; - LOG_INFO("gpio i2c type=%s port=%d addr=%u timeout=%u: in_pins=" GPIO_PINS_FMT " out_pins=" GPIO_PINS_FMT " inverted_pins=" GPIO_PINS_FMT, config_enum_to_string(i2c_gpio_type_enum, i2c_options->type) ?: "?", i2c_options->port, i2c_options->addr, - atx_psu_gpio_options.i2c_timeout, + i2c_options->timeout, GPIO_PINS_ARGS(atx_psu_gpio_options.in_pins), GPIO_PINS_ARGS(atx_psu_gpio_options.out_pins), GPIO_PINS_ARGS(atx_psu_gpio_options.inverted_pins) diff --git a/main/i2c_gpio.c b/main/i2c_gpio.c index 0f2830e5..b9e8c2d5 100644 --- a/main/i2c_gpio.c +++ b/main/i2c_gpio.c @@ -22,11 +22,13 @@ .type = GPIO_I2C_TYPE_PCA9534, .port = I2C_MASTER_PORT, .addr = CONFIG_I2C_GPIO_ADDR_PCA9534, + .timeout = (CONFIG_I2C_GPIO_TIMEOUT / portTICK_RATE_MS), .int_pin = CONFIG_I2C_GPIO_INT_PIN, #elif CONFIG_I2C_GPIO_TYPE_PCA9554 .type = GPIO_I2C_TYPE_PCA9554, .port = I2C_MASTER_PORT, .addr = CONFIG_I2C_GPIO_ADDR_PCA9554, + .timeout = (CONFIG_I2C_GPIO_TIMEOUT / portTICK_RATE_MS), .int_pin = CONFIG_I2C_GPIO_INT_PIN, #else #error "Invalid I2C_GPIO_TYPE configured" @@ -99,6 +101,7 @@ .type = config->type, .port = I2C_MASTER_PORT, .addr = config->addr, + .timeout = (CONFIG_I2C_GPIO_TIMEOUT / portTICK_RATE_MS), .int_pin = config->int_pin, }; diff --git a/main/leds_gpio.c b/main/leds_gpio.c index 2948f094..ad22ca5d 100644 --- a/main/leds_gpio.c +++ b/main/leds_gpio.c @@ -9,10 +9,6 @@ #if CONFIG_LEDS_GPIO_ENABLED - #if GPIO_I2C_ENABLED - #define LEDS_GPIO_I2C_TIMEOUT (20 / portTICK_RATE_MS) - #endif - // by interface struct gpio_options leds_gpio_options[LEDS_INTERFACE_COUNT] = { #if CONFIG_LEDS_SPI_ENABLED @@ -104,8 +100,6 @@ case GPIO_TYPE_I2C: i2c_options = gpio_i2c_options(options->i2c_dev); - options->i2c_timeout = LEDS_GPIO_I2C_TIMEOUT; - LOG_INFO("leds gpio[%s]: i2c type=%s port=%u addr=%u: out_pins=" GPIO_PINS_FMT " inverted_pins=" GPIO_PINS_FMT, config_enum_to_string(leds_interface_enum, interface), config_enum_to_string(i2c_gpio_type_enum, i2c_options->type) ?: "?", diff --git a/main/user_leds.c b/main/user_leds.c index 8266d255..5763c3bb 100644 --- a/main/user_leds.c +++ b/main/user_leds.c @@ -46,10 +46,6 @@ bool user_leds_override[USER_LEDS_COUNT]; QueueHandle_t user_leds_input_queue; // config -#if GPIO_I2C_ENABLED - #define USER_LEDS_GPIO_I2C_TIMEOUT (20 / portTICK_RATE_MS) -#endif - #define USER_LED_MODE_OUTPUT_BITS (USER_LEDS_MODE_OUTPUT_BIT) #if CONFIG_STATUS_LEDS_USER_MODE_TEST #define USER_LED_MODE_INPUT_BITS (USER_LEDS_MODE_INPUT_BIT | USER_LEDS_MODE_INTERRUPT_BIT) @@ -109,7 +105,6 @@ static struct gpio_options user_leds_gpio = { .type = GPIO_TYPE_HOST, #elif CONFIG_STATUS_LEDS_GPIO_TYPE_I2C_GPIO_0 .type = GPIO_TYPE_I2C, - .i2c_timeout = USER_LEDS_GPIO_I2C_TIMEOUT, #else #error "Invalid STATUS_LEDS_GPIO_TYPE" #endif @@ -166,7 +161,7 @@ int init_user_leds() } #ifdef USER_LEDS_I2C_GPIO_DEV - // requires init_i2c_gpio() + // after init_i2c_gpio() user_leds_gpio.i2c_dev = USER_LEDS_I2C_GPIO_DEV; #endif @@ -177,9 +172,7 @@ int init_user_leds() #if GPIO_I2C_ENABLED case GPIO_TYPE_I2C: - LOG_INFO("i2c gpio: timeout=%d", - user_leds_gpio.i2c_timeout - ); + LOG_INFO("i2c gpio"); break; #endif } From 211a16f5b26d0d876601ead2c8a02bbf99ff2989 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 1 Sep 2024 16:45:26 +0300 Subject: [PATCH 06/11] gpio: clear pca95xx input interrupts on init --- components/gpio/gpio.h | 2 +- components/gpio/i2c.c | 2 +- components/gpio/i2c_pca55xx.c | 22 +++++++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/components/gpio/gpio.h b/components/gpio/gpio.h index 8239d1c9..dd7eda63 100644 --- a/components/gpio/gpio.h +++ b/components/gpio/gpio.h @@ -65,7 +65,7 @@ void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, struct gpio_i2c_dev *i2c_dev); int gpio_i2c_set(const struct gpio_options *options, gpio_pins_t pins); /* gpio_i2c_pca54xx.c */ - int gpio_i2c_pca54xx_init(struct gpio_i2c_pca54xx_state *state); + int gpio_i2c_pca54xx_init(struct gpio_i2c_dev *i2c_dev); int gpio_i2c_pca54xx_setup(const struct gpio_options *options); int gpio_i2c_pca54xx_setup_input(const struct gpio_options *options, gpio_pins_t pins); int gpio_i2c_pca54xx_get(const struct gpio_options *options, gpio_pins_t *pins); diff --git a/components/gpio/i2c.c b/components/gpio/i2c.c index 1cc537ad..4fbf0c7d 100644 --- a/components/gpio/i2c.c +++ b/components/gpio/i2c.c @@ -49,7 +49,7 @@ switch(options->type) { case GPIO_I2C_TYPE_PCA9534: case GPIO_I2C_TYPE_PCA9554: - return gpio_i2c_pca54xx_init(&dev->state.pca54xx); + return gpio_i2c_pca54xx_init(dev); default: LOG_FATAL("unsupported type=%d", options->type); diff --git a/components/gpio/i2c_pca55xx.c b/components/gpio/i2c_pca55xx.c index 07d4f0b2..51714656 100644 --- a/components/gpio/i2c_pca55xx.c +++ b/components/gpio/i2c_pca55xx.c @@ -48,6 +48,18 @@ return 0; } + static int gpio_i2c_pca54xx_clear(const struct gpio_i2c_dev *dev) + { + uint8_t value; + int err; + + if ((err = gpio_i2c_pca54xx_read(&dev->options, PCA55XX_CMD_INPUT_PORT, &value))) { + return err; + } + + return 0; + } + static int gpio_i2c_pca54xx_input(const struct gpio_i2c_dev *dev, uint8_t mask, uint8_t *valuep) { uint8_t value; @@ -112,12 +124,20 @@ return err; } - int gpio_i2c_pca54xx_init(struct gpio_i2c_pca54xx_state *state) + int gpio_i2c_pca54xx_init(struct gpio_i2c_dev *i2c_dev) { + struct gpio_i2c_pca54xx_state *state = &i2c_dev->state.pca54xx; + int err; + state->output = 0xff; state->inversion = 0x00; state->config = 0xff; + // clear interrupts + if ((err = gpio_i2c_pca54xx_clear(i2c_dev))) { + return err; + } + return 0; } From 7e6b7cd2f4ab76aad0950acccab8f9b7c8d69e36 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 19 Oct 2024 16:44:46 +0300 Subject: [PATCH 07/11] atx_psu: move gpio_setup() to atx_psu_init() --- components/atx_psu/atx_psu.c | 34 +++++++++++++++------- components/atx_psu/include/atx_psu.h | 4 +-- main/atx_psu.c | 8 ++---- main/atx_psu_gpio.c | 42 ++++++++++------------------ main/atx_psu_gpio.h | 3 +- 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index d4a5ac20..7b832695 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -35,9 +35,23 @@ struct atx_psu { } atx_psu; -static int atx_psu_init(struct atx_psu *atx_psu, struct atx_psu_options options) +static IRAM_ATTR void atx_psu_gpio_interrupt(gpio_pins_t pins, void *arg) { - atx_psu->options = options; + struct atx_psu *atx_psu = arg; + + LOG_ISR_DEBUG("pins=" GPIO_PINS_FMT, GPIO_PINS_ARGS(pins)); +} + +static int atx_psu_init(struct atx_psu *atx_psu, const struct atx_psu_options *options) +{ + int err; + + atx_psu->options = *options; + + if ((err = gpio_setup(&atx_psu->options.gpio_options))) { + LOG_ERROR("gpio_setup"); + return err; + } if (!(atx_psu->state = xEventGroupCreate())) { LOG_ERROR("xEventGroupCreate"); @@ -52,7 +66,7 @@ static int atx_psu_init(struct atx_psu *atx_psu, struct atx_psu_options options) return 0; } -int atx_psu_new(struct atx_psu **atx_psup, struct atx_psu_options options) +int atx_psu_new(struct atx_psu **atx_psup, const struct atx_psu_options *options) { struct atx_psu *atx_psu; int err = 0; @@ -79,22 +93,22 @@ int atx_psu_new(struct atx_psu **atx_psup, struct atx_psu_options options) static void set_power_enable(struct atx_psu *atx_psu) { - LOG_DEBUG("pins=" GPIO_PINS_FMT " -> high", GPIO_PINS_ARGS(atx_psu->options.gpio_options->out_pins)); + LOG_DEBUG("pins=" GPIO_PINS_FMT " -> high", GPIO_PINS_ARGS(atx_psu->options.gpio_options.out_pins)); xEventGroupSetBits(atx_psu->status, ATX_PSU_STATUS_POWER_ENABLE_BIT); - if (gpio_out_set_all(atx_psu->options.gpio_options)) { + if (gpio_out_set_all(&atx_psu->options.gpio_options)) { LOG_WARN("gpio_out_set_all"); } } static void clear_power_enable(struct atx_psu *atx_psu) { - LOG_DEBUG("pins=" GPIO_PINS_FMT " -> low", GPIO_PINS_ARGS(atx_psu->options.gpio_options->out_pins)); + LOG_DEBUG("pins=" GPIO_PINS_FMT " -> low", GPIO_PINS_ARGS(atx_psu->options.gpio_options.out_pins)); xEventGroupClearBits(atx_psu->status, ATX_PSU_STATUS_POWER_ENABLE_BIT | ATX_PSU_STATUS_POWER_GOOD_BIT); - if (gpio_out_clear(atx_psu->options.gpio_options)) { + if (gpio_out_clear(&atx_psu->options.gpio_options)) { LOG_WARN("gpio_out_clear"); } } @@ -104,11 +118,11 @@ static bool get_power_good(struct atx_psu *atx_psu) gpio_pins_t pins; int err; - if (!atx_psu->options.gpio_options->in_pins) { + if (!atx_psu->options.gpio_options.in_pins) { LOG_DEBUG("pins=" GPIO_PINS_FMT " unknown -> true", GPIO_PINS_ARGS(pins)); xEventGroupSetBits(atx_psu->status, ATX_PSU_STATUS_POWER_GOOD_BIT); return true; - } else if ((err = gpio_in_get(atx_psu->options.gpio_options, &pins))) { + } else if ((err = gpio_in_get(&atx_psu->options.gpio_options, &pins))) { LOG_DEBUG("pins=" GPIO_PINS_FMT " error -> true", GPIO_PINS_ARGS(pins)); return true; } else if (pins) { @@ -238,7 +252,7 @@ int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); - if (!atx_psu->options.gpio_options->in_pins) { + if (!atx_psu->options.gpio_options.in_pins) { LOG_DEBUG("power_good_gpio disabled"); return ATX_PSU_STATUS_POWER_GOOD_BIT; diff --git a/components/atx_psu/include/atx_psu.h b/components/atx_psu/include/atx_psu.h index 10fa3dbf..7ef56f29 100644 --- a/components/atx_psu/include/atx_psu.h +++ b/components/atx_psu/include/atx_psu.h @@ -37,7 +37,7 @@ struct atx_psu_options { * * Use <0 to disable. */ - const struct gpio_options *gpio_options; + struct gpio_options gpio_options; /* * Return ATX PSU into standby mode once all bits are inactive, and the timeout has passed. @@ -45,7 +45,7 @@ struct atx_psu_options { TickType_t timeout; }; -int atx_psu_new(struct atx_psu **atx_psup, struct atx_psu_options options); +int atx_psu_new(struct atx_psu **atx_psup, const struct atx_psu_options *options); /* Run as a separate task */ void atx_psu_main(void *arg); diff --git a/main/atx_psu.c b/main/atx_psu.c index 4ccbb003..78f4ba9e 100644 --- a/main/atx_psu.c +++ b/main/atx_psu.c @@ -24,16 +24,14 @@ int init_atx_psu() return 0; } - if ((err = init_atx_psu_gpio(&atx_psu_config))) { - LOG_ERROR("init_atx_psu_gpio"); + if ((err = config_atx_psu_gpio(&atx_psu_config, &options.gpio_options))) { + LOG_ERROR("config_atx_psu_gpio"); return err; } - config_atx_psu_gpio(&atx_psu_config, &options); - LOG_INFO("timeout=%u", options.timeout); - if (atx_psu_new(&atx_psu, options)) { + if (atx_psu_new(&atx_psu, &options)) { LOG_ERROR("atx_psu_new"); return -1; } diff --git a/main/atx_psu_gpio.c b/main/atx_psu_gpio.c index 0416e9c3..21beb1e0 100644 --- a/main/atx_psu_gpio.c +++ b/main/atx_psu_gpio.c @@ -4,15 +4,11 @@ #include -struct gpio_options atx_psu_gpio_options = { - -}; - -int init_atx_psu_gpio(const struct atx_psu_config *config) +int config_atx_psu_gpio(const struct atx_psu_config *config, struct gpio_options *gpio_options) { int err; - if ((err = set_gpio_type(&atx_psu_gpio_options, config->gpio_type))) { + if ((err = set_gpio_type(gpio_options, config->gpio_type))) { LOG_ERROR("invalid gpio_type=%d", config->gpio_type); return err; } @@ -24,10 +20,10 @@ int init_atx_psu_gpio(const struct atx_psu_config *config) config->power_enable_gpio ); - atx_psu_gpio_options.out_pins |= gpio_host_pin(config->power_enable_gpio); + gpio_options->out_pins |= gpio_host_pin(config->power_enable_gpio); if (config->power_enable_gpio_mode == ATX_PSU_GPIO_MODE_LOW) { - atx_psu_gpio_options.inverted_pins |= gpio_host_pin(config->power_enable_gpio); + gpio_options->inverted_pins |= gpio_host_pin(config->power_enable_gpio); } } @@ -38,10 +34,10 @@ int init_atx_psu_gpio(const struct atx_psu_config *config) config->power_good_gpio ); - atx_psu_gpio_options.in_pins |= gpio_host_pin(config->power_good_gpio); + gpio_options->in_pins |= gpio_host_pin(config->power_good_gpio); if (config->power_good_gpio_mode == ATX_PSU_GPIO_MODE_LOW) { - atx_psu_gpio_options.inverted_pins |= gpio_host_pin(config->power_good_gpio); + gpio_options->inverted_pins |= gpio_host_pin(config->power_good_gpio); } } @@ -49,41 +45,31 @@ int init_atx_psu_gpio(const struct atx_psu_config *config) const struct gpio_i2c_options *i2c_options; #endif - switch(atx_psu_gpio_options.type) { + switch(gpio_options->type) { case GPIO_TYPE_HOST: LOG_INFO("gpio host: in_pins=" GPIO_PINS_FMT " out_pins=" GPIO_PINS_FMT " inverted_pins=" GPIO_PINS_FMT, - GPIO_PINS_ARGS(atx_psu_gpio_options.in_pins), - GPIO_PINS_ARGS(atx_psu_gpio_options.out_pins), - GPIO_PINS_ARGS(atx_psu_gpio_options.inverted_pins) + GPIO_PINS_ARGS(gpio_options->in_pins), + GPIO_PINS_ARGS(gpio_options->out_pins), + GPIO_PINS_ARGS(gpio_options->inverted_pins) ); break; #if GPIO_I2C_ENABLED case GPIO_TYPE_I2C: - i2c_options = gpio_i2c_options(atx_psu_gpio_options.i2c_dev); + i2c_options = gpio_i2c_options(gpio_options->i2c_dev); LOG_INFO("gpio i2c type=%s port=%d addr=%u timeout=%u: in_pins=" GPIO_PINS_FMT " out_pins=" GPIO_PINS_FMT " inverted_pins=" GPIO_PINS_FMT, config_enum_to_string(i2c_gpio_type_enum, i2c_options->type) ?: "?", i2c_options->port, i2c_options->addr, i2c_options->timeout, - GPIO_PINS_ARGS(atx_psu_gpio_options.in_pins), - GPIO_PINS_ARGS(atx_psu_gpio_options.out_pins), - GPIO_PINS_ARGS(atx_psu_gpio_options.inverted_pins) + GPIO_PINS_ARGS(gpio_options->in_pins), + GPIO_PINS_ARGS(gpio_options->out_pins), + GPIO_PINS_ARGS(gpio_options->inverted_pins) ); break; #endif } - if ((err = gpio_setup(&atx_psu_gpio_options))) { - LOG_ERROR("gpio_setup"); - return err; - } - return 0; } - -void config_atx_psu_gpio(const struct atx_psu_config *config, struct atx_psu_options *options) -{ - options->gpio_options = &atx_psu_gpio_options; -} diff --git a/main/atx_psu_gpio.h b/main/atx_psu_gpio.h index 686dd7ea..adce0594 100644 --- a/main/atx_psu_gpio.h +++ b/main/atx_psu_gpio.h @@ -4,5 +4,4 @@ #include -int init_atx_psu_gpio(const struct atx_psu_config *config); -void config_atx_psu_gpio(const struct atx_psu_config *config, struct atx_psu_options *options); +int config_atx_psu_gpio(const struct atx_psu_config *config, struct gpio_options *gpio_options); From be360f51d0ef1f8cafc79bd348546d5c5fdb014c Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 19 Oct 2024 19:54:42 +0300 Subject: [PATCH 08/11] atx_psu: add gpio interrupt handler --- components/atx_psu/atx_psu.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index 7b832695..b929bb7c 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -38,8 +38,17 @@ struct atx_psu { static IRAM_ATTR void atx_psu_gpio_interrupt(gpio_pins_t pins, void *arg) { struct atx_psu *atx_psu = arg; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; LOG_ISR_DEBUG("pins=" GPIO_PINS_FMT, GPIO_PINS_ARGS(pins)); + +#if CONFIG_IDF_TARGET_ESP8266 + if (xHigherPriorityTaskWoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +#else + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +#endif } static int atx_psu_init(struct atx_psu *atx_psu, const struct atx_psu_options *options) @@ -48,6 +57,11 @@ static int atx_psu_init(struct atx_psu *atx_psu, const struct atx_psu_options *o atx_psu->options = *options; + // use interrupts for power_good input pins + atx_psu->options.gpio_options.interrupt_pins = atx_psu->options.gpio_options.in_pins; + atx_psu->options.gpio_options.interrupt_func = atx_psu_gpio_interrupt; + atx_psu->options.gpio_options.interrupt_arg = atx_psu; + if ((err = gpio_setup(&atx_psu->options.gpio_options))) { LOG_ERROR("gpio_setup"); return err; From 85b945464a30eb17ab9d1f6d6f21dc0a93f018f6 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 19 Oct 2024 23:50:53 +0300 Subject: [PATCH 09/11] atx_psu: split events, bits and status -> state, tick --- components/atx_psu/atx_psu.c | 225 ++++++++++++++++++++++++----------- 1 file changed, 154 insertions(+), 71 deletions(-) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index b929bb7c..6fb6d958 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -8,30 +8,48 @@ #include -#define ATX_PSU_STATE_CLEAR_BIT (1 << 0) -#define ATX_PSU_STATE_BIT(bit) (1 << (bit + 1)) -#define ATX_PSU_STATE_BITS ((1 << ATX_PSU_BIT_COUNT) - 1 - 1) // all bits, but not the clear bit +#define ATX_PSU_BIT(bit) (1 << (bit)) +#define ATX_PSU_BITS ((1 << ATX_PSU_BIT_COUNT) - 1) + +#define ATX_PSU_EVENTS_SET_BIT (1 << 0) +#define ATX_PSU_EVENTS_CLEAR_BIT (1 << 1) +#define ATX_PSU_EVENTS_INTR_BIT (1 << 2) #define ATX_PSU_STATUS_POWER_ENABLE_BIT (1 << 0) #define ATX_PSU_STATUS_POWER_GOOD_BIT (1 << 1) -// poll for PG while ACTIVATE -> ACTIVE -#define ATX_PSU_POWER_GOOD_ACTIVATE_TICKS (100 / portTICK_PERIOD_MS) - -// poll for PG while ACTIVATE -#define ATX_PSU_POWER_GOOD_ACTIVE_TICKS (1000 / portTICK_PERIOD_MS) +// polling for PG +#define ATX_PSU_INACTIVE_TICKS (10000 / portTICK_PERIOD_MS) +#define ATX_PSU_ACTIVATE_TICKS (100 / portTICK_PERIOD_MS) +#define ATX_PSU_ACTIVE_TICKS (1000 / portTICK_PERIOD_MS) enum atx_psu_state { - INACTIVE, - ACTIVATE, - ACTIVE, - DEACTIVATE, + INACTIVE, // waiting for any bit set + ACTIVATE, // waiting for power-good, or bits cleared + ACTIVE, // waiting for bits cleared, checking for power-bad + DEACTIVATE, // waiting for delayed power-off, or any bit set }; +static const char *atx_psu_state_str(enum atx_psu_state state) +{ + switch (state) { + case INACTIVE: return "INACTIVE"; + case ACTIVATE: return "ACTIVATE"; + case ACTIVE: return "ACTIVE"; + case DEACTIVATE: return "DEACTIVATE"; + default: return "?"; + } +} + struct atx_psu { struct atx_psu_options options; + enum atx_psu_state state; + TickType_t tick; - EventGroupHandle_t state, status; + EventGroupHandle_t events; + + // XXX: these are just atomic bitmasks + EventGroupHandle_t bits, status; } atx_psu; @@ -42,6 +60,10 @@ static IRAM_ATTR void atx_psu_gpio_interrupt(gpio_pins_t pins, void *arg) LOG_ISR_DEBUG("pins=" GPIO_PINS_FMT, GPIO_PINS_ARGS(pins)); + if (!xEventGroupSetBitsFromISR(atx_psu->events, ATX_PSU_EVENTS_INTR_BIT, &xHigherPriorityTaskWoken)) { + LOG_ISR_WARN("xEventGroupSetBitsFromISR"); + } + #if CONFIG_IDF_TARGET_ESP8266 if (xHigherPriorityTaskWoken == pdTRUE) { portYIELD_FROM_ISR(); @@ -67,7 +89,12 @@ static int atx_psu_init(struct atx_psu *atx_psu, const struct atx_psu_options *o return err; } - if (!(atx_psu->state = xEventGroupCreate())) { + if (!(atx_psu->events = xEventGroupCreate())) { + LOG_ERROR("xEventGroupCreate"); + return -1; + } + + if (!(atx_psu->bits = xEventGroupCreate())) { LOG_ERROR("xEventGroupCreate"); return -1; } @@ -150,121 +177,177 @@ static bool get_power_good(struct atx_psu *atx_psu) } } -static EventBits_t atx_psu_wait(struct atx_psu *atx_psu, TickType_t ticks) +static void next_state(struct atx_psu *atx_psu, enum atx_psu_state state, TickType_t ticks) { - LOG_DEBUG("ticks=%d", ticks); + EventBits_t bits = xEventGroupGetBits(atx_psu->bits); + + LOG_DEBUG("%s -> %s<%08x> @ %d", atx_psu_state_str(atx_psu->state), atx_psu_state_str(state), bits, ticks); - // wait for any bit except clear, do not clear - return xEventGroupWaitBits(atx_psu->state, ATX_PSU_STATE_BITS, pdFALSE, pdFALSE, ticks) & ATX_PSU_STATE_BITS; + atx_psu->state = state; + atx_psu->tick = (ticks == portMAX_DELAY) ? 0 : xTaskGetTickCount() + ticks; } -static EventBits_t atx_psu_wait_clear(struct atx_psu *atx_psu, TickType_t ticks) +static EventBits_t atx_psu_wait(struct atx_psu *atx_psu) { - LOG_DEBUG("ticks=%d", ticks); + EventBits_t events = 0; + + // always follow input interrupts + events |= ATX_PSU_EVENTS_INTR_BIT; + + switch(atx_psu->state) { + case INACTIVE: + // indefinite wait for any bit to be set + events |= ATX_PSU_EVENTS_SET_BIT; + break; + + case ACTIVATE: + // fast wait for power-good + events |= ATX_PSU_EVENTS_CLEAR_BIT; + break; + + case ACTIVE: + // slow wait for power-bad + events |= ATX_PSU_EVENTS_CLEAR_BIT; + break; + + case DEACTIVATE: + // configurable wait for bits to remain cleared + events |= ATX_PSU_EVENTS_SET_BIT; + break; + + default: + LOG_FATAL("state=%d", atx_psu->state); + } - // wait for clear bit and clear it - return xEventGroupWaitBits(atx_psu->state, ATX_PSU_STATE_CLEAR_BIT, pdTRUE, pdTRUE, ticks) & ATX_PSU_STATE_BITS; + // schedule + TickType_t ticks, tick = xTaskGetTickCount(); + + if (!atx_psu->tick) { + ticks = portMAX_DELAY; // indefinite + } else if (atx_psu->tick > tick) { + ticks = atx_psu->tick - tick; + } else { + ticks = 0; // immediate + } + + LOG_DEBUG("state=%s events=%04x ticks=%d", atx_psu_state_str(atx_psu->state), events, ticks); + + // wait for any bit and clear + const BaseType_t xClearOnExit = pdTRUE; + const BaseType_t xWaitForAllBits = pdFALSE; + + events = xEventGroupWaitBits(atx_psu->events, events, xClearOnExit, xWaitForAllBits, ticks); + + // check state tick timeout + if (!atx_psu->tick) { + return false; + } else if (xTaskGetTickCount() >= atx_psu->tick) { + return true; + } else { + return false; + } } void atx_psu_main(void *arg) { struct atx_psu *atx_psu = arg; - enum atx_psu_state state = INACTIVE; for (;;) { - EventBits_t bits; + bool timeout = atx_psu_wait(atx_psu); + EventBits_t bits = xEventGroupGetBits(atx_psu->bits); + bool power_good = get_power_good(atx_psu); + + LOG_DEBUG("state=%s bits=%04x power_good=%d timeout=%d", atx_psu_state_str(atx_psu->state), bits, power_good, timeout); - switch(state) { + switch(atx_psu->state) { case INACTIVE: - LOG_DEBUG("INACTIVE..."); + if (bits) { + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); - // indefinite wait for any bit - if ((bits = atx_psu_wait(atx_psu, portMAX_DELAY))) { - LOG_DEBUG("INACTIVE -> ACTIVATE<%08x>", bits); - set_power_enable(atx_psu); - state = ACTIVATE; LOG_INFO("power-on"); + set_power_enable(atx_psu); + } else { - LOG_DEBUG("INACTIVE"); + next_state(atx_psu, INACTIVE, ATX_PSU_INACTIVE_TICKS); } break; case ACTIVATE: - LOG_DEBUG("ACTIVATE..."); - // poll for PG - if (!(bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_ACTIVATE_TICKS))) { - LOG_DEBUG("ACTIVATE -> DEACTIVATE"); + if (!bits) { // delay deactivation - state = DEACTIVATE; - } else if (get_power_good(atx_psu)) { - LOG_DEBUG("ACTIVATE -> ACTIVE<%08x>", bits); - state = ACTIVE; + next_state(atx_psu, DEACTIVATE, atx_psu->options.timeout); + + } else if (power_good) { LOG_INFO("power-good"); - } else { + + next_state(atx_psu, ACTIVE, ATX_PSU_ACTIVE_TICKS); + + } else if (timeout) { // re-ACTIVATE to test power_good - LOG_DEBUG("ACTIVATE"); + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); } break; case ACTIVE: - LOG_DEBUG("ACTIVE..."); - // wait for clear bit, poll for PG - if (!(bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_ACTIVE_TICKS))) { - LOG_DEBUG("ACTIVE -> DEACTIVATE"); + if (!bits) { // delay deactivation - state = DEACTIVATE; - } else if (get_power_good(atx_psu)) { - LOG_DEBUG("ACTIVE<%08x>", bits); - } else { + next_state(atx_psu, DEACTIVATE, atx_psu->options.timeout); + + } else if (!power_good) { + LOG_WARN("power-bad"); + // re-ACTIVATE to test power_good - LOG_DEBUG("ACTIVE -> ACTIVATE"); - state = ACTIVATE; - LOG_WARN("lost power-good!"); + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); + + } else if (timeout) { + next_state(atx_psu, ACTIVE, ATX_PSU_ACTIVE_TICKS); } break; case DEACTIVATE: - LOG_DEBUG("DEACTIVATE..."); - // wait for any bit or deactivate on timeout - if (!(bits = atx_psu_wait(atx_psu, atx_psu->options.timeout))) { - LOG_DEBUG("DEACTIVATE -> INACTIVE"); - clear_power_enable(atx_psu); - state = INACTIVE; + if (bits && power_good) { + // remain active + next_state(atx_psu, ACTIVE, ATX_PSU_ACTIVE_TICKS); + } else if (bits) { + // re-ACTIVATE to test power_good + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); + } else if (timeout) { LOG_INFO("power-off"); - } else if (get_power_good(atx_psu)) { - LOG_DEBUG("DEACTIVATE -> ACTIVE<%08x>", bits); - state = ACTIVE; + clear_power_enable(atx_psu); + + next_state(atx_psu, INACTIVE, ATX_PSU_INACTIVE_TICKS); } else { - LOG_DEBUG("DEACTIVATE -> ACTIVATE<%08x>", bits); - state = ACTIVATE; + // remain in DEACTIVATE state } break; default: - abort(); + LOG_FATAL("state=%d", atx_psu->state); } } } void atx_psu_power_enable(struct atx_psu *atx_psu, enum atx_psu_bit bit) { - LOG_DEBUG("bit=%08x", ATX_PSU_STATE_BIT(bit)); + LOG_DEBUG("bit=%08x", ATX_PSU_BIT(bit)); - xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); + xEventGroupSetBits(atx_psu->bits, ATX_PSU_BIT(bit)); + xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_SET_BIT); } int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t timeout) { EventBits_t bits; - xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); + xEventGroupSetBits(atx_psu->bits, ATX_PSU_BIT(bit)); + xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_SET_BIT); if (!atx_psu->options.gpio_options.in_pins) { LOG_DEBUG("power_good_gpio disabled"); @@ -281,8 +364,8 @@ int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t void atx_psu_standby(struct atx_psu *atx_psu, enum atx_psu_bit bit) { - LOG_DEBUG("bit=%08x", ATX_PSU_STATE_BIT(bit)); + LOG_DEBUG("bit=%08x", ATX_PSU_BIT(bit)); - xEventGroupClearBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); - xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_CLEAR_BIT); + xEventGroupClearBits(atx_psu->bits, ATX_PSU_BIT(bit)); + xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_CLEAR_BIT); } From a264306711cae60c4f13699f72d4619dea0b9902 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 20 Oct 2024 00:46:30 +0300 Subject: [PATCH 10/11] gpio: fix esp8266 --- components/gpio/esp8266/gpio_host.c | 2 +- components/gpio/esp8266/gpio_intr.c | 5 +++-- components/gpio/gpio.h | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/gpio/esp8266/gpio_host.c b/components/gpio/esp8266/gpio_host.c index ed472471..264c0b74 100644 --- a/components/gpio/esp8266/gpio_host.c +++ b/components/gpio/esp8266/gpio_host.c @@ -171,7 +171,7 @@ int gpio_host_setup(const struct gpio_options *options) } if (interrupt) { if (GPIO_HOST_PINS_HOST & GPIO_PINS(gpio)) { - gpio_intr_setup_pin(options, gpio); + gpio_intr_setup_host_pin(gpio, options); } } } diff --git a/components/gpio/esp8266/gpio_intr.c b/components/gpio/esp8266/gpio_intr.c index 3c05802d..72a58524 100644 --- a/components/gpio/esp8266/gpio_intr.c +++ b/components/gpio/esp8266/gpio_intr.c @@ -26,7 +26,8 @@ static IRAM_ATTR void gpio_isr (void *arg) #if GPIO_I2C_ENABLED case GPIO_TYPE_I2C: - gpio_i2c_intr_handler(options, pins); + // TODO: + gpio_i2c_intr_handler(i2c_dev, pins); break; #endif } @@ -44,7 +45,7 @@ int gpio_intr_init() return 0; } -void gpio_intr_setup_pin(const struct gpio_options *options, gpio_pin_t gpio) +void gpio_intr_setup_host_pin(gpio_pin_t gpio, const struct gpio_options *options) { if (gpio >= 16) { LOG_WARN("[%d] -> %p: invalid gpio_pin_t", gpio, options); diff --git a/components/gpio/gpio.h b/components/gpio/gpio.h index dd7eda63..74cec041 100644 --- a/components/gpio/gpio.h +++ b/components/gpio/gpio.h @@ -48,7 +48,9 @@ int gpio_host_set_all(const struct gpio_options *options); /* gpio_intr.c */ int gpio_intr_init(); void gpio_intr_setup_host_pin(gpio_pin_t gpio, const struct gpio_options *options); -void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, struct gpio_i2c_dev *i2c_dev); +#if GPIO_I2C_ENABLED + void gpio_intr_setup_i2c_pin(gpio_pin_t gpio, struct gpio_i2c_dev *i2c_dev); +#endif #if !CONFIG_IDF_TARGET_ESP8266 int gpio_intr_core(); From 55d15eff6bae6db8bcd62b6b1858783c3b6d852c Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sun, 20 Oct 2024 01:40:00 +0300 Subject: [PATCH 11/11] atx_psu: debug atx_psu_power_good() calls too --- components/atx_psu/atx_psu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index 6fb6d958..1d98f26e 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -344,7 +344,7 @@ void atx_psu_power_enable(struct atx_psu *atx_psu, enum atx_psu_bit bit) int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t timeout) { - EventBits_t bits; + LOG_DEBUG("bit=%08x", ATX_PSU_BIT(bit)); xEventGroupSetBits(atx_psu->bits, ATX_PSU_BIT(bit)); xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_SET_BIT); @@ -354,7 +354,7 @@ int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t return ATX_PSU_STATUS_POWER_GOOD_BIT; } else { - bits = xEventGroupWaitBits(atx_psu->status, ATX_PSU_STATUS_POWER_ENABLE_BIT | ATX_PSU_STATUS_POWER_GOOD_BIT, pdFALSE, pdTRUE, timeout); + EventBits_t bits = xEventGroupWaitBits(atx_psu->status, ATX_PSU_STATUS_POWER_ENABLE_BIT | ATX_PSU_STATUS_POWER_GOOD_BIT, pdFALSE, pdTRUE, timeout); //LOG_DEBUG("bits=%08x", bits);