diff --git a/firmware/config/boards/kinetis/rusefi_hw_enums.h b/firmware/config/boards/kinetis/rusefi_hw_enums.h index a22468dd5c..d41f3ee178 100644 --- a/firmware/config/boards/kinetis/rusefi_hw_enums.h +++ b/firmware/config/boards/kinetis/rusefi_hw_enums.h @@ -145,6 +145,20 @@ typedef enum __attribute__ ((__packed__)) { /* Plase keep updating this define */ #define BRAIN_PIN_LAST_ONCHIP GPIOE_17 +/* diagnostic for brain pins + * can be combination of few bits + * defined as bit mask */ +typedef enum __attribute__ ((__packed__)) +{ + PIN_OK = 0, + PIN_OPEN = 0x01, + PIN_SHORT_TO_GND = 0x02, + PIN_SHORT_TO_BAT = 0x04, + PIN_OVERLOAD = 0x08, + PIN_DRIVER_OVERTEMP = 0x10, + PIN_INVALID = 0x80 +} brain_pin_diag_e; + typedef enum __attribute__ ((__packed__)) { EFI_ADC_0 = 0, EFI_ADC_1 = 1, diff --git a/firmware/controllers/algo/rusefi_hw_enums.h b/firmware/controllers/algo/rusefi_hw_enums.h index f77dc16854..0dff07fada 100644 --- a/firmware/controllers/algo/rusefi_hw_enums.h +++ b/firmware/controllers/algo/rusefi_hw_enums.h @@ -219,6 +219,20 @@ typedef enum __attribute__ ((__packed__)) { /* Plase keep updating this define */ #define BRAIN_PIN_LAST_ONCHIP GPIOH_15 +/* diagnostic for brain pins + * can be combination of few bits + * defined as bit mask */ +typedef enum __attribute__ ((__packed__)) +{ + PIN_OK = 0, + PIN_OPEN = 0x01, + PIN_SHORT_TO_GND = 0x02, + PIN_SHORT_TO_BAT = 0x04, + PIN_OVERLOAD = 0x08, + PIN_DRIVER_OVERTEMP = 0x10, + PIN_INVALID = 0x80 +} brain_pin_diag_e; + /** * Frankenso analog #1 PC2 ADC12 * Frankenso analog #2 PC1 ADC11 diff --git a/firmware/hw_layer/drivers/gpio/core.c b/firmware/hw_layer/drivers/gpio/core.c index de6ac43962..355712d43a 100644 --- a/firmware/hw_layer/drivers/gpio/core.c +++ b/firmware/hw_layer/drivers/gpio/core.c @@ -282,6 +282,27 @@ int gpiochips_readPad(brain_pin_e pin) return -1; } +/** + * @brief Get diagnostic for given gpio + * @details actual output value depent on gpiochip capabilities + * returns -1 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) +{ + struct gpiochip *chip = gpiochip_find(pin); + + if (!chip) + return PIN_INVALID; + + if (chip->ops->getDiag) + return chip->ops->getDiag(chip->priv, pin - chip->base); + + return PIN_OK; +} + /** * @brief Get total pin count allocated for external gpio chips. * @details Will also include unused pins for chips that was registered diff --git a/firmware/hw_layer/drivers/gpio/gpio_ext.h b/firmware/hw_layer/drivers/gpio/gpio_ext.h index 061b355857..4d6b4eb927 100644 --- a/firmware/hw_layer/drivers/gpio/gpio_ext.h +++ b/firmware/hw_layer/drivers/gpio/gpio_ext.h @@ -30,7 +30,7 @@ struct gpiochip_ops { int (*setPadMode)(void *data, unsigned int pin, int mode); int (*writePad)(void *data, unsigned int pin, int value); int (*readPad)(void *data, unsigned int pin); - int (*getDiag)(void *data, unsigned int pin); + brain_pin_diag_e (*getDiag)(void *data, unsigned int pin); int (*init)(void *data); int (*deinit)(void *data); }; @@ -53,6 +53,7 @@ int gpiochips_init(void); int gpiochips_setPadMode(brain_pin_e pin, int mode); int gpiochips_writePad(brain_pin_e pin, int value); int gpiochips_readPad(brain_pin_e pin); +brain_pin_diag_e gpiochips_getDiag(brain_pin_e pin); /* return total number of external gpios */ int gpiochips_get_total_pins(void); diff --git a/firmware/hw_layer/drivers/gpio/mc33810.c b/firmware/hw_layer/drivers/gpio/mc33810.c index 140af6617e..298be0eb6f 100644 --- a/firmware/hw_layer/drivers/gpio/mc33810.c +++ b/firmware/hw_layer/drivers/gpio/mc33810.c @@ -52,6 +52,9 @@ typedef enum { #define CMD_PWM(pwm) (0xa000 | ((pwm) & 0x0fff)) #define CMD_CLK_CALIB (0xe000) +/* get command code from TX value */ +#define TX_GET_CMD(v) (((v) >> 12) & 0x000f) + /* enum? */ #define REG_ALL_STAT (0x0) #define REG_OUT10_FAULT (0x1) @@ -95,6 +98,18 @@ struct mc33810_priv { /* direct driven output mask */ uint8_t o_direct_mask; + /* ALL STATUS RESPONSE value and flags */ + bool all_status_requested; + bool all_status_updated; + uint16_t all_status_value; + + /* OUTx fault registers */ + uint16_t out_fault[2]; + /* GPGD mode fault register */ + uint16_t gpgd_fault; + /* IGN mode fault register */ + uint16_t ign_fault; + mc33810_drv_state drv_state; }; @@ -144,6 +159,17 @@ static int mc33810_spi_rw(struct mc33810_priv *chip, uint16_t tx, uint16_t *rx) if (rx) *rx = rxb; + /* if reply on previous command is ALL STATUS RESPONSE */ + if (chip->all_status_requested) { + chip->all_status_value = rxb; + chip->all_status_updated = true; + } + + /* if next reply will be ALL STATUS RESPONSE */ + chip->all_status_requested = + (((TX_GET_CMD(tx) >= 0x1) && (TX_GET_CMD(tx) <= 0xa)) || + (tx == CMD_READ_REG(REG_ALL_STAT))); + /* no errors for now */ return 0; } @@ -153,13 +179,77 @@ static int mc33810_spi_rw(struct mc33810_priv *chip, uint16_t tx, uint16_t *rx) * @details Sends ORed data to register, also receive diagnostic. */ -static int mc33810_update_output(struct mc33810_priv *chip) +static int mc33810_update_output_and_diag(struct mc33810_priv *chip) { int ret = 0; /* TODO: lock? */ - chip->o_state_cached = chip->o_state; + /* we need to get updates status */ + chip->all_status_updated = false; + + /* if any pin is driven over SPI */ + if (chip->o_direct_mask != 0xff) { + uint16_t out_data; + + out_data = chip->o_state & (~chip->o_direct_mask); + ret = mc33810_spi_rw(chip, CMD_DRIVER_EN(out_data), NULL); + if (ret) + return ret; + chip->o_state_cached = chip->o_state; + } + + /* this comlicated logic to save few spi transfers in case we will receive status as reply on other command */ + if (!chip->all_status_requested) { + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_ALL_STAT), NULL); + if (ret) + return ret; + } + /* get reply */ + if (!chip->all_status_updated) { + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_ALL_STAT), NULL); + if (ret) + return ret; + } + /* now we have updated ALL STATUS register in chip data */ + + /* check OUT (injectors) first */ + if (chip->all_status_value & 0x000f) { + /* request diagnostic of OUT0 and OUT1 */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_OUT10_FAULT), NULL); + if (ret) + return ret; + /* get diagnostic for OUT0 and OUT1 and request diagnostic for OUT2 and OUT3 */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_OUT32_FAULT), &chip->out_fault[0]); + if (ret) + return ret; + /* get diagnostic for OUT2 and OUT2 and requset ALL STATUS */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_ALL_STAT), &chip->out_fault[1]); + if (ret) + return ret; + } + /* check GPGD - mode not supported yet */ + if (chip->all_status_value & 0x00f0) { + /* request diagnostic of GPGD */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_GPGD_FAULT), NULL); + if (ret) + return ret; + /* get diagnostic for GPGD and requset ALL STATUS */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_ALL_STAT), &chip->gpgd_fault); + if (ret) + return ret; + } + /* check IGN */ + if (chip->all_status_value & 0x0f00) { + /* request diagnostic of IGN */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_IGN_FAULT), NULL); + if (ret) + return ret; + /* get diagnostic for IGN and requset ALL STATUS */ + ret = mc33810_spi_rw(chip, CMD_READ_REG(REG_ALL_STAT), &chip->ign_fault); + if (ret) + return ret; + } /* TODO: unlock? */ @@ -300,7 +390,7 @@ static THD_FUNCTION(mc33810_driver_thread, p) continue; /* TODO: implemet indirect driven gpios */ - ret = mc33810_update_output(chip); + ret = mc33810_update_output_and_diag(chip); if (ret) { /* set state to MC33810_FAILED? */ } @@ -349,16 +439,45 @@ int mc33810_writePad(void *data, unsigned int pin, int value) return 0; } -int mc33810_getDiag(void *data, unsigned int pin) +brain_pin_diag_e mc33810_getDiag(void *data, unsigned int pin) { - int diag; + int val; + struct mc33810_priv *chip; + brain_pin_diag_e diag = PIN_OK; if ((pin >= MC33810_DIRECT_OUTPUTS) || (data == NULL)) - return -1; + return PIN_INVALID; - /* TODO: implement */ - diag = 0; + chip = (struct mc33810_priv *)data; + if (pin < 4) { + /* OUT drivers */ + val = chip->out_fault[pin < 2 ? 0 : 1] >> (4 * (pin & 0x01)); + + /* ON open fault */ + if (val & BIT(0)) + diag |= PIN_OPEN; + /* OFF open fault */ + if (val & BIT(1)) + diag |= PIN_OPEN; + if (val & BIT(2)) + diag |= PIN_SHORT_TO_BAT; + if (val & BIT(3)) + diag |= PIN_DRIVER_OVERTEMP; + } else { + /* INJ drivers, GPGD mode is not supported */ + val = chip->ign_fault >> (3 * (pin - 4)); + + /* open load */ + if (val & BIT(0)) + diag |= PIN_OPEN; + /* max Dwell fault - too long coil charge time */ + if (val & BIT(1)) + diag |= PIN_OVERLOAD; + /* MAXI fault - too high coil current */ + if (val & BIT(2)) + diag |= PIN_OVERLOAD; + } /* convert to some common enum? */ return diag; } @@ -437,14 +556,21 @@ int mc33810_add(unsigned int index, const struct mc33810_config *cfg) chip->o_direct_mask |= (1 << i); } - chip->drv_state = MC33810_WAIT_INIT; + /* GPGD mode is not supported yet, ignition mode does not support spi on/off commands + * so ignition signals should be directly driven */ + if ((chip->o_direct_mask & 0xf0) != 0xf0) + return -1; /* register, return gpio chip base */ ret = gpiochip_register(DRIVER_NAME, &mc33810_ops, MC33810_OUTPUTS, chip); + if (ret < 0) + return ret; /* set default pin names, board init code can rewrite */ gpiochips_setPinNames(ret, mc33810_pin_names); + chip->drv_state = MC33810_WAIT_INIT; + return ret; } diff --git a/firmware/hw_layer/drivers/gpio/mc33810.h b/firmware/hw_layer/drivers/gpio/mc33810.h index 2ce205e38f..c9b9c45c2a 100644 --- a/firmware/hw_layer/drivers/gpio/mc33810.h +++ b/firmware/hw_layer/drivers/gpio/mc33810.h @@ -25,6 +25,7 @@ struct mc33810_config { SPIDriver *spi_bus; SPIConfig spi_config; + /* First 4 is injector drivers, then 4 ignition pre-drivers */ struct { ioportid_t port; uint_fast8_t pad; diff --git a/firmware/hw_layer/drivers/gpio/mc33972.c b/firmware/hw_layer/drivers/gpio/mc33972.c index b529281a23..a1ba74f41d 100644 --- a/firmware/hw_layer/drivers/gpio/mc33972.c +++ b/firmware/hw_layer/drivers/gpio/mc33972.c @@ -273,19 +273,19 @@ int mc33972_readPad(void *data, unsigned int pin) { return !!(chip->i_state & FLAG_PIN(pin)); } -int mc33972_getDiag(void *data, unsigned int pin) { - int diag; +brain_pin_diag_e mc33972_getDiag(void *data, unsigned int pin) { + brain_pin_diag_e diag = PIN_OK; struct mc33972_priv *chip; if ((pin >= MC33972_INPUTS) || (data == NULL)) - return -1; + return PIN_INVALID; chip = (struct mc33972_priv *)data; - /* one diag for all pins */ - diag = !!(chip->i_state & FLAG_THERM); + /* one diag bit for all pins */ + if (chip->i_state & FLAG_THERM) + diag = PIN_DRIVER_OVERTEMP; - /* convert to some common enum? */ return diag; } diff --git a/firmware/hw_layer/drivers/gpio/tle6240.c b/firmware/hw_layer/drivers/gpio/tle6240.c index 9cf6bdcc26..e79ded3497 100644 --- a/firmware/hw_layer/drivers/gpio/tle6240.c +++ b/firmware/hw_layer/drivers/gpio/tle6240.c @@ -409,19 +409,28 @@ int tle6240_writePad(void *data, unsigned int pin, int value) return 0; } -int tle6240_getDiag(void *data, unsigned int pin) +brain_pin_diag_e tle6240_getDiag(void *data, unsigned int pin) { - int diag; + int val; + brain_pin_diag_e diag; struct tle6240_priv *chip; - if ((pin >= TLE6240_DIRECT_OUTPUTS) || (data == NULL)) - return -1; + if ((pin >= TLE6240_OUTPUTS) || (data == NULL)) + return PIN_INVALID; chip = (struct tle6240_priv *)data; - diag = (chip->diag[(pin > 7) ? 1 : 0] >> ((pin % 8) * 2)) & 0x03; + val = (chip->diag[(pin > 7) ? 1 : 0] >> ((pin % 8) * 2)) & 0x03; + if (val == 0x3) + diag = PIN_OK; + else if (val == 0x2) + /* Overload, shorted load or overtemperature */ + diag = PIN_OVERLOAD | PIN_DRIVER_OVERTEMP; + else if (val == 0x1) + diag = PIN_OPEN; + else if (val == 0x0) + diag = PIN_SHORT_TO_GND; - /* convert to some common enum? */ return diag; } diff --git a/firmware/hw_layer/pin_repository.cpp b/firmware/hw_layer/pin_repository.cpp index 0855be862e..f3b750766a 100644 --- a/firmware/hw_layer/pin_repository.cpp +++ b/firmware/hw_layer/pin_repository.cpp @@ -68,24 +68,41 @@ static void reportPins(void) { #if (BOARD_EXT_GPIOCHIPS > 0) for (unsigned int i = getNumBrainPins() ; i < getNumBrainPins() + BOARD_EXT_PINREPOPINS /* gpiochips_get_total_pins()*/ ; i++) { + static char pin_error[64]; const char *pin_name; const char *pin_user; + brain_pin_diag_e pin_diag; brain_pin_e brainPin = index_to_brainPin(i); pin_name = gpiochips_getPinName(brainPin); pin_user = getBrainUsedPin(i); + pin_diag = gpiochips_getDiag(brainPin); + + /* use autogeneraged helpers here? */ + if (pin_diag == PIN_OK) { + snprintf(pin_error, sizeof(pin_error), "Ok"); + } else if (pin_diag != PIN_INVALID) { + snprintf(pin_error, sizeof(pin_error), "%s%s%s%s%s", + pin_diag & PIN_OPEN ? "open_load " : "", + pin_diag & PIN_SHORT_TO_GND ? "short_to_gnd " : "", + pin_diag & PIN_SHORT_TO_BAT ? "short_to_bat " : "", + pin_diag & PIN_OVERLOAD ? "overload " : "", + pin_diag & PIN_DRIVER_OVERTEMP ? "overtemp": ""); + } else { + snprintf(pin_error, sizeof(pin_error), "INVALID"); + } /* here show all pins, unused too */ if (pin_name != NULL) { // this probably uses a lot of output buffer! - scheduleMsg(&logger, "ext %s: %s", - pin_name, pin_user ? pin_user : "free"); + scheduleMsg(&logger, "ext %s: %s diagnostic: %s", + pin_name, pin_user ? pin_user : "free", pin_error); } else { const char *chip_name = gpiochips_getChipName(brainPin); /* if chip exist */ if (chip_name != NULL) { - scheduleMsg(&logger, "ext %s.%d: %s", - chip_name, gpiochips_getPinOffset(brainPin), pin_user ? pin_user : "free"); + scheduleMsg(&logger, "ext %s.%d: %s diagnostic: %s", + chip_name, gpiochips_getPinOffset(brainPin), pin_user ? pin_user : "free", pin_error); } } }