L9779: initial driver (#3897)

* smart_gpio.cpp: glue to L9779

* L9779: fill with some code

* L9779: missed getDiag

* L9779: no l9779 on subaru-eg33 board
This commit is contained in:
Andrey G 2022-02-06 17:47:18 +03:00 committed by GitHub
parent ba65d56563
commit a50f848857
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 333 additions and 4 deletions

View File

@ -22,6 +22,9 @@ DDEFS += -DHW_SUBARU_EG33=1
# Override DEFAULT_ENGINE_TYPE
DDEFS += -DDEFAULT_ENGINE_TYPE=SUBARUEG33_DEFAULTS
# HW options
DDEFS += -DBOARD_L9779_COUNT=0
#Some options override
#ICU vs PAL/EXTI
DDEFS += -DHAL_TRIGGER_USE_PAL=TRUE

View File

@ -6,8 +6,295 @@
#include "pch.h"
#include "gpio/l9779.h"
#if (BOARD_L9779_COUNT > 0)
#include "persistent_configuration.h"
#include "hardware.h"
#include "gpio/gpio_ext.h"
#include "os_util.h"
/*
* TODO list:
* - just write code
*/
/*==========================================================================*/
/* Driver local definitions. */
/*==========================================================================*/
#define DRIVER_NAME "l9779"
/* SPI communication helpers */
/* Out frame */
/* D0 - parity */
/* D8:D1 - DATA OUT or SUBADDRESS if ADD[4:0] = 0x10 (for read) */
#define MSG_SET_DATA(d) (((d) & 0xff) << 1)
/* sub-address is 5 bit */
#define MSG_SET_SUBADDR(s) (((s) & 0x1f) << 1)
/* D9 - x */
/* D14:D10 - ADDRESS */
#define MSG_SET_ADDR(a) (((a) & 0x1f) << 10)
/* D15 - x */
/* In frame */
/* D0 - parity */
#define MSG_GET_PARITY(rx) (((rx) >> 0) & 0x01)
/* D8:D1 - DATA IN */
#define MSG_GET_DATA(rx) (((rx) >> 1) & 0xff)
/* D 9 - W/R flag, 1 if we read */
#define MSG_GET_WR(rx) (((rx) >> 9) & 0x01)
/* D14:D10 - Addr of DATA IN */
#define MSG_GET_ADDR(rx) (((rx) >> 10) & 0x1f)
/* D15 - SPI error flag */
#define MSG_GET_SPIERROR(rx) (((rx) >> 15) & 0x01)
/* register address that never can be replyed */
#define REG_INVALID 0xff
/*==========================================================================*/
/* Driver exported variables. */
/*==========================================================================*/
/*==========================================================================*/
/* Driver local variables and types. */
/*==========================================================================*/
/* Driver private data */
struct L9779 : public GpioChip {
int init() override;
int deinit() override;
int setPadMode(size_t pin, iomode_t mode) override;
int writePad(size_t pin, int value) override;
int readPad(size_t pin) override;
brain_pin_diag_e getDiag(size_t pin) override;
bool spi_parity_odd(uint16_t x);
int spi_validate(uint16_t rx);
int spi_rw(uint16_t tx, uint16_t *rx_ptr);
brain_pin_diag_e getOutputDiag(size_t pin);
brain_pin_diag_e getInputDiag(size_t pin);
const l9779_config *cfg;
/* last accessed register, for validation on next SPI access */
uint8_t last_reg;
/* statistic */
//int por_cnt;
//int wdr_cnt;
//int comfe_cnt;
//int init_cnt;
//int init_req_cnt;
int spi_cnt;
int spi_err_parity; /* parity errors in rx data */
int spi_err_frame; /* rx messages with bit 15 set */
int spi_err; /* rx messages with incorrect ADDR or WR fields */
uint16_t tx;
uint16_t rx;
};
static L9779 chips[BOARD_L9779_COUNT];
static const char* l9779_pin_names[L9779_SIGNALS] = {
"L9779.IGN1", "L9779.IGN2", "L9779.IGN3", "L9779.IGN4",
"L9779.OUT1", "L9779.OUT2", "L9779.OUT3", "L9779.OUT4",
"L9779.OUT5", "L9779.OUT6", "L9779.OUT7", "L9779.OUT8",
"L9779.OUT9", "L9779.OUT10", "L9779.OUT11", "L9779.OUT12",
"L9779.OUT13", "L9779.OUT14", "L9779.OUT15", "L9779.OUT16",
"L9779.OUT17", "L9779.OUT18", "L9779.OUT19", "L9779.OUT20",
"L9779.OUTA", "L9779.OUTB", "L9779.OUTC", "L9779.OUTD",
"L9779.OUT21", "L9779.OUT22", "L9779.OUT23", "L9779.OUT24",
"L9779.MRD", "L9779.KEY"
};
/*==========================================================================*/
/* Driver local functions. */
/*==========================================================================*/
/* true if parity of input x is odd */
bool L9779::spi_parity_odd(uint16_t x)
{
x ^= x >> 8;
x ^= x >> 4;
x ^= x >> 2;
x ^= x >> 1;
return (x & 1);
}
int L9779::spi_validate(uint16_t rx)
{
if (!spi_parity_odd(rx)) {
spi_err_parity++;
return -1;
}
if (MSG_GET_SPIERROR(rx)) {
/* not clear what does this means */
spi_err_frame++;
return -1;
}
uint8_t reg = MSG_GET_DATA(rx);
/* TODO: also check WR bit */
if ((last_reg != REG_INVALID) && (last_reg != reg)) {
/* unexpected SPI answer */
spi_err++;
/* should ve restart? */
//need_init = true;
return -1;
}
return 0;
}
/**
* @returns -1 in case of communication error
*/
int L9779::spi_rw(uint16_t tx, uint16_t *rx_ptr)
{
int ret;
uint16_t rx;
SPIDriver *spi = cfg->spi_bus;
/* set parity */
tx |= !spi_parity_odd(tx);
/* Acquire ownership of the bus. */
spiAcquireBus(spi);
/* Setup transfer parameters. */
spiStart(spi, &cfg->spi_config);
/* Slave Select assertion. */
spiSelect(spi);
/* Atomic transfer operations. */
rx = spiPolledExchange(spi, tx);
/* Slave Select de-assertion. */
spiUnselect(spi);
/* Ownership release. */
spiReleaseBus(spi);
/* statisctic and debug */
this->tx = tx;
this->rx = rx;
this->spi_cnt++;
if (rx_ptr)
*rx_ptr = rx;
/* validate reply and save last accessed register */
ret = spi_validate(rx);
last_reg = MSG_GET_ADDR(tx);
/* no errors for now */
return ret;
}
/*==========================================================================*/
/* Driver interrupt handlers. */
/*==========================================================================*/
/*==========================================================================*/
/* Driver exported functions. */
/*==========================================================================*/
int L9779::setPadMode(unsigned int pin, iomode_t mode) {
if (pin >= L9779_SIGNALS)
return -1;
(void)mode;
return 0;
}
int L9779::writePad(unsigned int pin, int value) {
if (pin >= L9779_OUTPUTS)
return -1;
(void)value;
return 0;
}
brain_pin_diag_e L9779::getOutputDiag(size_t pin)
{
(void)pin;
return PIN_OK;
}
brain_pin_diag_e L9779::getInputDiag(unsigned int pin)
{
(void)pin;
return PIN_OK;
}
int L9779::readPad(size_t pin) {
if (pin >= L9779_SIGNALS)
return -1;
/* unknown pin */
return -1;
}
brain_pin_diag_e L9779::getDiag(size_t pin)
{
if (pin >= TLE8888_SIGNALS)
return PIN_INVALID;
if (pin < L9779_OUTPUTS)
return getOutputDiag(pin);
else
return getInputDiag(pin);
}
int L9779::init()
{
return 0;
}
int L9779::deinit()
{
return 0;
}
/**
* @brief L9779 driver add.
* @details Checks for valid config
* @return return gpio chip base
*/
int l9779_add(brain_pin_e base, unsigned int index, const l9779_config *cfg) {
efiAssert(OBD_PCM_Processor_Fault, cfg != NULL, "L9779CFG", 0)
/* no config or no such chip */
if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_L9779_COUNT))
return -1;
L9779* chip = &chips[index];
/* already initted? */
if (chip->cfg)
return -1;
/* config */
chip->cfg = cfg;
/* reset to defaults */
/* register */
int ret = gpiochip_register(base, DRIVER_NAME, *chip, L9779_SIGNALS);
if (ret < 0)
return ret;
/* set default pin names, board init code can rewrite */
gpiochips_setPinNames(base, l9779_pin_names);
return ret;
}
#endif /* (BOARD_L9779_COUNT > 0) */

View File

@ -8,11 +8,22 @@
#pragma once
#include "efifeatures.h"
#include "global.h"
#include <hal.h>
#include "efifeatures.h"
struct l9779_config {
#if HAL_USE_SPI
SPIDriver *spi_bus;
SPIConfig spi_config;
#endif
};
#define L9779_OUTPUTS_IGN (4)
#define L9779_OUTPUTS (L9779_OUTPUTS_IGN + 28 + 1)
#define L9779_INPUTS (1)
#define L9779_SIGNALS (L9779_OUTPUTS + L9779_INPUTS)
int l9779_add(brain_pin_e base, unsigned int index, const l9779_config *cfg);

View File

@ -100,8 +100,23 @@ struct mc33972_config mc33972 = {
static OutputPin l9779Cs;
struct l9779_config l9779_cfg = {
.spi_bus = NULL,
.spi_config = {
.circular = false,
.end_cb = NULL,
.ssport = NULL,
.sspad = 0,
.cr1 =
SPI_CR1_16BIT_MODE |
SPI_CR1_SSM |
SPI_CR1_SSI |
//SPI_CR1_LSBFIRST | //MSB first
((3 << SPI_CR1_BR_Pos) & SPI_CR1_BR) | // div = 16, up to 8 MHz
SPI_CR1_MSTR |
SPI_CR1_CPHA |
0,
.cr2 = SPI_CR2_16BIT_MODE
},
};
#endif /* (BOARD_L9779_COUNT > 0) */
#if (BOARD_TLE8888_COUNT > 0)
@ -206,6 +221,19 @@ void initSmartGpio() {
}
#endif /* (BOARD_MC33972_COUNT > 0) */
#if (BOARD_L9779_COUNT > 0)
if (isBrainPinValid(engineConfiguration->l9779_cs)) {
// todo: reuse initSpiCs method?
l9779_cfg.spi_config.ssport = getHwPort("l9779 CS", engineConfiguration->l9779_cs);
l9779_cfg.spi_config.sspad = getHwPin("l9779 CS", engineConfiguration->l9779_cs);
l9779_cfg.spi_bus = getSpiDevice(engineConfiguration->l9779spiDevice);
// todo: propogate 'basePinOffset' parameter
int ret = l9779_add(L9779_IGN_1, 0, &l9779_cfg);
efiAssertVoid(OBD_PCM_Processor_Fault, ret == L9779_IGN_1, "l9779");
}
#endif /* (BOARD_L9779_COUNT > 0) */
#if (BOARD_TLE8888_COUNT > 0)
if (isBrainPinValid(engineConfiguration->tle8888_cs)) {
// todo: reuse initSpiCs method?