/** * @file gpio/core.c * @brief EFI-related GPIO code for external gpio chips * * @date Mar 8, 2019 * @author Andrey Gusakov, (c) 2019 */ #include "pch.h" #include "gpio/gpio_ext.h" #include "smart_gpio.h" #define STRING2(x) #x #define STRING(x) STRING2(x) #pragma message(STRING(BOARD_EXT_GPIOCHIPS)) #if (BOARD_EXT_GPIOCHIPS > 0) /*==========================================================================*/ /* Local definitions. */ /*==========================================================================*/ /*==========================================================================*/ /* Exported variables. */ /*==========================================================================*/ /*==========================================================================*/ /* Local variables and types. */ /*==========================================================================*/ /* TODO: chnage array to list? */ struct gpiochip { brain_pin_e base; size_t size; GpioChip *chip; const char *name; /* optional names of each gpio */ const char **gpio_names; }; static gpiochip chips[BOARD_EXT_GPIOCHIPS]; /*==========================================================================*/ /* Local functions. */ /*==========================================================================*/ /** * @return pointer to GPIO device for specified pin */ static gpiochip *gpiochip_find(brain_pin_e pin) { for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) { gpiochip *chip = &chips[i]; if ((pin >= chip->base) && (pin < (chip->base + chip->size))) return chip; } return nullptr; } /*==========================================================================*/ /* Exported functions. */ /*==========================================================================*/ /** * return numeric part of EXTERNAL pin name. * @details */ int gpiochips_getPinOffset(brain_pin_e pin) { gpiochip *chip = gpiochip_find(pin); if (chip) return pin - chip->base; return EFI_ERROR_CODE; } /** * @brief Get external chip name * @details return gpiochip name */ const char *gpiochips_getChipName(brain_pin_e pin) { gpiochip *chip = gpiochip_find(pin); if (chip) return chip->name; return nullptr; } /** * @brief Get external chip port name * @details return pin name or gpiochip name (if no pins names provided) */ const char *gpiochips_getPinName(brain_pin_e pin) { int offset; gpiochip *chip = gpiochip_find(pin); if (chip) { offset = pin - chip->base; if ((chip->gpio_names) && (chip->gpio_names[offset])) return chip->gpio_names[offset]; } return nullptr; } /** * @brief Register gpiochip * @details should be called from board file. Can be called before os ready. * All chips should be registered before gpiochips_init() called. * returns -101 in case of no free chips left * returns -100 in case of no ops provided, incorrect chip size * returns -102 or -103 in case chip overlaps already registered chip(s) * else returns chip base */ int gpiochip_register(brain_pin_e base, const char *name, GpioChip& gpioChip, size_t size) { /* zero size? */ if (!size) return -100; /* outside? */ if ((base + size - 1 > BRAIN_PIN_LAST) || (base <= BRAIN_PIN_ONCHIP_LAST)) return -101; /* check for overlap with other chips */ for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) { if (chips[i].base != Gpio::Unassigned) { #define in_range(a, b, c) (((a) > (b)) && ((a) < (c))) if (in_range(base, chips[i].base, chips[i].base + chips[i].size)) return -102; if (in_range(base + size, chips[i].base, chips[i].base + chips[i].size)) return -103; } } gpiochip *chip = nullptr; /* find free gpiochip struct */ for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) { if (chips[i].base == Gpio::Unassigned) { chip = &chips[i]; break; } } /* no free chips left */ if (!chip) { return -104; } /* register chip */ chip->name = name; chip->chip = &gpioChip; chip->base = base; chip->size = size; chip->gpio_names = nullptr; // TODO: this cast seems wrong? return (int)base; } /** * @brief Unregister gpiochip * @details removes chip from list * TODO: call deinit? */ int gpiochip_unregister(brain_pin_e base) { gpiochip *chip = gpiochip_find(base); if (!chip) return -105; /* gpiochip_find - returns chip if base within its range, but we need it to be base */ if (chip->base != base) return -106; /* unregister chip */ chip->name = nullptr; chip->chip = nullptr; chip->base = Gpio::Unassigned; chip->size = 0; chip->gpio_names = nullptr; return 0; } /** * @brief Set pins names for registered gpiochip * @details should be called after chip registration. May be called several times * Names array size should be aqual to chip gpio count */ int gpiochips_setPinNames(brain_pin_e base, const char **names) { gpiochip *chip = gpiochip_find(base); if (!chip) return -113; chip->gpio_names = names; return 0; } /** * @brief Init all registered gpiochips * @details will call gpiochip init ops for all registered chips * calles when OS is ready, so gpiochip can start threads, use drivers and so on. */ int gpiochips_init(void) { int pins_added = 0; for (int i = 0; i < BOARD_EXT_GPIOCHIPS; i++) { gpiochip *chip = &chips[i]; if (chip->base == Gpio::Unassigned) continue; int ret = chip->chip->init(); if (ret < 0) { #if EFI_PROD_CODE // todo: adjust unit tests to validate this fatal criticalError("Failed to init chip %d: %d", i, ret); #else chip->base = Gpio::Unassigned; #endif } else { pins_added += chip->size; } } return pins_added; } /** * @brief Set pin mode of gpiochip * @details set pad mode for given pin. * return -107 if driver does not implemet setPadMode ops * else return value from gpiochip driver. */ /* this fuction uses iomode_t that is related to STM32 (or other MCU) * output modes. Use some common enums? */ int gpiochips_setPadMode(brain_pin_e pin, iomode_t mode) { gpiochip *chip = gpiochip_find(pin); if (!chip) return -107; return chip->chip->setPadMode(pin - chip->base, mode); } /** * @brief Set value to gpio of gpiochip * @details actual output value depent on current gpiochip implementation * for smart switch inactive supposed to be closed switch (no current flows) * returns -1 in case of pin not belong to any gpio chip * returns -1 in case of chip does not support seting output value (input only) * else return value from gpiochip driver; */ int gpiochips_writePad(brain_pin_e pin, int value) { gpiochip *chip = gpiochip_find(pin); if (!chip) { // todo: make readPad fail in a similar way? criticalError("gpiochip not found for pin %d", pin); return -108; } return chip->chip->writePad(pin - chip->base, value); } /** * @brief Get value to gpio of gpiochip * @details actual input value depent on current gpiochip implementation * returns -1 in case of pin not belong to any gpio chip * returns -1 in case of chip does not support getting output value (output only) * else return value from gpiochip driver; */ int gpiochips_readPad(brain_pin_e pin) { gpiochip *chip = gpiochip_find(pin); if (!chip) return -109; return chip->chip->readPad(pin - chip->base); } /** * @brief Get diagnostic for given gpio * @details actual output value depend on gpiochip capabilities * returns PIN_UNKNOWN in case of pin not belong to any gpio chip * returns PIN_OK in case of chip does not support getting diagnostic * else return brain_pin_diag_e from gpiochip driver; */ brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin) { gpiochip *chip = gpiochip_find(pin); if (!chip) return PIN_UNKNOWN; return chip->chip->getDiag(pin - chip->base); } /** * @brief Get total pin count allocated for external gpio chips. * @details Will also include unused pins for chips that was registered * but later fails to init. */ int gpiochips_get_total_pins(void) { int i; int cnt = 0; for (i = 0; i < BOARD_EXT_GPIOCHIPS; i++) { gpiochip *chip = &chips[i]; if (chip->base == Gpio::Unassigned) continue; cnt += chip->size; } return cnt; } #else /* BOARD_EXT_GPIOCHIPS > 0 */ int gpiochips_getPinOffset(brain_pin_e pin) { (void)pin; return -111; } const char *gpiochips_getChipName(brain_pin_e pin) { (void)pin; return nullptr; } const char *gpiochips_getPinName(brain_pin_e pin) { (void)pin; return nullptr; } int gpiochip_register(brain_pin_e base, const char *name, GpioChip&, size_t size) { (void)base; (void)name; (void)size; return 0; } int gpiochips_setPinNames(brain_pin_e pin, const char **names) { (void)pin; (void)names; return 0; } int gpiochips_init(void) { return 0; } int gpiochips_get_total_pins(void) { return 0; } #endif /* BOARD_EXT_GPIOCHIPS > 0 */