2019-04-04 06:55:18 -07:00
|
|
|
/*
|
|
|
|
* tle8888.c
|
|
|
|
*
|
|
|
|
* TLE8888 Engine Machine System IC driver
|
|
|
|
*
|
|
|
|
* @date Mar 25, 2019
|
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2019
|
|
|
|
*
|
|
|
|
* Andrey Gusakov, (c) 2019
|
|
|
|
*/
|
|
|
|
|
2019-04-04 07:08:50 -07:00
|
|
|
#include "global.h"
|
|
|
|
|
|
|
|
#if EFI_TLE8888 || defined(__DOXYGEN__)
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/* to be removed */
|
|
|
|
#include "engine_configuration.h"
|
|
|
|
|
2019-04-04 16:29:33 -07:00
|
|
|
EXTERN_CONFIG;
|
|
|
|
|
|
|
|
#include "hardware.h"
|
2019-04-04 16:56:03 -07:00
|
|
|
#include "efi_gpio.h"
|
2019-04-04 06:55:18 -07:00
|
|
|
#include "gpio/gpio_ext.h"
|
|
|
|
#include "gpio/tle8888.h"
|
|
|
|
#include "pin_repository.h"
|
|
|
|
|
2019-04-04 20:03:32 -07:00
|
|
|
#if EFI_TUNER_STUDIO || defined(__DOXYGEN__)
|
|
|
|
#include "tunerstudio.h"
|
|
|
|
extern TunerStudioOutputChannels tsOutputChannels;
|
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/* TODO: move to board.h file */
|
|
|
|
#define BOARD_TLE8888_COUNT 1
|
|
|
|
|
|
|
|
#ifndef BOARD_TLE8888_COUNT
|
|
|
|
#define BOARD_TLE8888_COUNT 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if (BOARD_TLE8888_COUNT > 0)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO list:
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver local definitions. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
#define DRIVER_NAME "tle8888"
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver exported variables. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
/* C0 */
|
|
|
|
#define CMD_READ (0 << 0)
|
|
|
|
#define CMD_WRITE (1 << 0)
|
|
|
|
/* C7:1 */
|
|
|
|
#define CMD_REG_ADDR(a) (((a) & 0x7f) << 1)
|
|
|
|
/* CD7:0 */
|
|
|
|
#define CMD_REG_DATA(d) (((d) & 0xff) << 8)
|
|
|
|
|
|
|
|
#define CMD_WR(a, d) (CMD_WRITE | CMD_REG_ADDR(a) | CMD_REG_DATA(d))
|
|
|
|
|
|
|
|
#define CMD_SR CMD_WR(0x1a, 0x03)
|
|
|
|
#define CMD_OE_SET CMD_WR(0x1c, 0x02)
|
|
|
|
#define CMD_OE_CLR CMD_WR(0x1c, 0x01)
|
|
|
|
#define CMD_LOCK CMD_WR(0x1e, 0x02)
|
|
|
|
#define CMD_UNLOCK CMD_WR(0x1e, 0x01)
|
|
|
|
#define CMD_INCONFIG(n, d) CMD_WR(0x53 + (n & 0x03), d)
|
|
|
|
#define CMD_DDCONFIG(n, d) CMD_WR(0x57 + (n & 0x03), d)
|
|
|
|
#define CMD_OECONFIG(n, d) CMD_WR(0x5b + (n & 0x03), d)
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver local variables and types. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
/* Driver private data */
|
|
|
|
struct tle8888_priv {
|
|
|
|
const struct tle8888_config *cfg;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct tle8888_priv chips[BOARD_TLE8888_COUNT];
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver local functions. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
static SPIDriver *get_bus(struct tle8888_priv *chip)
|
|
|
|
{
|
|
|
|
/* return non-const SPIDriver* from const struct cfg */
|
|
|
|
return chip->cfg->spi_bus;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tle8888_spi_rw(struct tle8888_priv *chip, uint16_t tx, uint16_t *rx)
|
|
|
|
{
|
|
|
|
|
|
|
|
uint16_t rxb;
|
|
|
|
SPIDriver *spi = get_bus(chip);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 15.1 SPI Protocol
|
|
|
|
*
|
|
|
|
* after a read or write command: the address and content of the selected register
|
|
|
|
* is transmitted with the next SPI transmission (for not existing addresses or
|
|
|
|
* wrong access mode the data is always 0)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Acquire ownership of the bus. */
|
|
|
|
spiAcquireBus(spi);
|
|
|
|
/* Setup transfer parameters. */
|
|
|
|
spiStart(spi, &chip->cfg->spi_config);
|
|
|
|
/* Slave Select assertion. */
|
|
|
|
spiSelect(spi);
|
|
|
|
/* Atomic transfer operations. */
|
2019-04-04 15:52:00 -07:00
|
|
|
// todo 16 bit for F4? rxb = spiPolledExchange(spi, tx);
|
|
|
|
spiExchange(spi, 2, &tx, &rxb);
|
2019-04-04 06:55:18 -07:00
|
|
|
/* Slave Select de-assertion. */
|
|
|
|
spiUnselect(spi);
|
|
|
|
/* Ownership release. */
|
|
|
|
spiReleaseBus(spi);
|
|
|
|
|
2019-04-04 20:03:32 -07:00
|
|
|
#if EFI_TUNER_STUDIO || defined(__DOXYGEN__)
|
|
|
|
if (engineConfiguration->debugMode == DBG_TLE8888) {
|
|
|
|
tsOutputChannels.debugIntField1++;
|
|
|
|
tsOutputChannels.debugIntField2 = tx;
|
|
|
|
tsOutputChannels.debugIntField3 = rxb;
|
|
|
|
}
|
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
if (rx)
|
|
|
|
*rx = rxb;
|
|
|
|
|
|
|
|
/* no errors for now */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver thread. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver interrupt handlers. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver exported functions. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
int tle8888_chip_init(void * data)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct tle8888_priv *chip = (struct tle8888_priv *)data;
|
|
|
|
const struct tle8888_config *cfg = chip->cfg;
|
2019-04-04 20:03:32 -07:00
|
|
|
efiAssert(OBD_PCM_Processor_Fault, cfg != NULL, "8888CFG", 0)
|
2019-04-04 06:55:18 -07:00
|
|
|
uint8_t dd[4] = {0, 0, 0, 0};
|
|
|
|
uint8_t oe[4] = {0, 0, 0, 0};
|
|
|
|
|
2019-04-04 20:03:32 -07:00
|
|
|
int ret = 0;
|
2019-04-04 06:55:18 -07:00
|
|
|
/* mark pins used */
|
2019-04-04 20:03:32 -07:00
|
|
|
// we do not initialize CS pin so we should not be marking it used
|
|
|
|
// ret = markUsed(cfg->spi_config.ssport, cfg->spi_config.sspad, DRIVER_NAME " CS");
|
2019-04-04 06:55:18 -07:00
|
|
|
if (cfg->reset.port != NULL)
|
|
|
|
ret |= markUsed(cfg->reset.port, cfg->reset.pad, DRIVER_NAME " RST");
|
2019-04-04 17:03:31 -07:00
|
|
|
/*
|
|
|
|
error: iteration 4 invokes undefined behavior [-Werror=aggressive-loop-optimizations]
|
2019-04-04 06:55:18 -07:00
|
|
|
for (i = 0; i < TLE8888_DIRECT_OUTPUTS; i++)
|
|
|
|
if (cfg->direct_io[i].port)
|
|
|
|
ret |= markUsed(cfg->direct_io[i].port, cfg->direct_io[i].pad, DRIVER_NAME " DIRECT IO");
|
2019-04-04 17:03:31 -07:00
|
|
|
*/
|
2019-04-04 06:55:18 -07:00
|
|
|
if (ret) {
|
|
|
|
ret = -1;
|
|
|
|
goto err_gpios;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Software reset */
|
|
|
|
tle8888_spi_rw(chip, CMD_SR, NULL);
|
|
|
|
|
|
|
|
/* delay? */
|
|
|
|
|
|
|
|
/* Set LOCK bit to 0 */
|
|
|
|
tle8888_spi_rw(chip, CMD_UNLOCK, NULL);
|
|
|
|
|
|
|
|
/* enable direct drive of OUTPUT4..1
|
|
|
|
* ...still need INJEN signal */
|
|
|
|
dd[0] |= 0x0f;
|
|
|
|
oe[0] |= 0x0f;
|
|
|
|
/* enable direct drive of IGN4..1
|
|
|
|
* ...still need IGNEN signal */
|
|
|
|
dd[3] |= 0x0f;
|
|
|
|
oe[3] |= 0x0f;
|
|
|
|
|
|
|
|
/* map and enable outputs for direct driven channels */
|
|
|
|
for (i = 0; i < TLE8888_DIRECT_MISC; i++) {
|
|
|
|
int reg;
|
|
|
|
int mask;
|
|
|
|
int out = cfg->direct_map[i];
|
|
|
|
|
|
|
|
/* OUT1..4 driven direct only */
|
|
|
|
if (out < 5)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
reg = (out - 1)/ 8;
|
|
|
|
mask = 1 << ((out - 1) % 8);
|
|
|
|
|
|
|
|
/* check if output already ocupied */
|
|
|
|
if (dd[reg] & mask) {
|
|
|
|
/* incorrect config? */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable direct drive and output enable */
|
|
|
|
dd[reg] |= mask;
|
|
|
|
oe[reg] |= mask;
|
|
|
|
|
|
|
|
tle8888_spi_rw(chip, CMD_INCONFIG(reg, out - 5), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set registers */
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
tle8888_spi_rw(chip, CMD_OECONFIG(i, oe[i]), NULL);
|
|
|
|
tle8888_spi_rw(chip, CMD_DDCONFIG(i, dd[i]), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable outputs */
|
|
|
|
tle8888_spi_rw(chip, CMD_OE_SET, NULL);
|
|
|
|
|
|
|
|
err_gpios:
|
|
|
|
/* unmark pins */
|
|
|
|
markUnused(cfg->spi_config.ssport, cfg->spi_config.sspad);
|
|
|
|
if (cfg->reset.port != NULL)
|
|
|
|
markUnused(cfg->reset.port, cfg->reset.pad);
|
2019-04-04 17:57:51 -07:00
|
|
|
/*
|
|
|
|
error: iteration 4 invokes undefined behavior [-Werror=aggressive-loop-optimizations]
|
2019-04-04 06:55:18 -07:00
|
|
|
for (i = 0; i < TLE8888_DIRECT_OUTPUTS; i++)
|
|
|
|
if (cfg->direct_io[i].port)
|
|
|
|
markUnused(cfg->direct_io[i].port, cfg->direct_io[i].pad);
|
2019-04-04 17:57:51 -07:00
|
|
|
*/
|
2019-04-04 06:55:18 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief TLE8888 driver add.
|
|
|
|
* @details Checks for valid config
|
|
|
|
*/
|
|
|
|
|
|
|
|
int tle8888_add(unsigned int index, const struct tle8888_config *cfg)
|
|
|
|
{
|
|
|
|
struct tle8888_priv *chip;
|
|
|
|
|
|
|
|
/* no config or no such chip */
|
|
|
|
osalDbgCheck((cfg != NULL) && (cfg->spi_bus != NULL) && (index < BOARD_TLE8888_COUNT));
|
|
|
|
|
|
|
|
/* check for valid cs.
|
2019-04-04 17:03:31 -07:00
|
|
|
* TODO: remove this check? CS can be driven by SPI */
|
2019-04-04 06:55:18 -07:00
|
|
|
if (cfg->spi_config.ssport == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
chip = &chips[index];
|
|
|
|
|
|
|
|
/* already initted? */
|
|
|
|
if (chip->cfg != NULL)
|
|
|
|
return -1;
|
2019-04-04 20:03:32 -07:00
|
|
|
chip->cfg = cfg;
|
2019-04-04 06:55:18 -07:00
|
|
|
|
|
|
|
/* TODO: remove this when gpiochips integrated */
|
|
|
|
return tle8888_chip_init(chip);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* BOARD_TLE8888_COUNT > 0 */
|
|
|
|
|
|
|
|
int tle8888_add(unsigned int index, const struct tle8888_config *cfg)
|
|
|
|
{
|
|
|
|
(void)index; (void)cfg;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* BOARD_TLE8888_COUNT */
|
|
|
|
|
|
|
|
/*********TO BE REMOVED FROM THIS FILE***************/
|
|
|
|
|
|
|
|
/* this should be in board file */
|
2019-04-04 16:29:33 -07:00
|
|
|
static struct tle8888_config tle8888_cfg = {
|
2019-04-04 16:56:03 -07:00
|
|
|
.spi_bus = NULL,
|
2019-04-04 06:55:18 -07:00
|
|
|
.spi_config = {
|
|
|
|
.circular = false,
|
|
|
|
.end_cb = NULL,
|
|
|
|
.ssport = GPIOF,
|
|
|
|
.sspad = 0U,
|
|
|
|
.cr1 =
|
|
|
|
/* TODO: set LSB first mode !!!! */
|
|
|
|
/* SPI_CR1_CPOL | */ // = 0
|
|
|
|
SPI_CR1_CPHA | // = 1
|
|
|
|
(((3 << SPI_CR1_BR_Pos) & SPI_CR1_BR) |
|
|
|
|
SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR),
|
|
|
|
/* 16-bit transfer */
|
2019-04-04 15:52:00 -07:00
|
|
|
.cr2 = 0/* not for F4? SPI_CR2_DS_3 | SPI_CR2_DS_2 | SPI_CR2_DS_1 | SPI_CR2_DS_0*/
|
2019-04-04 06:55:18 -07:00
|
|
|
},
|
|
|
|
/* not implemented yet, use STM32 gpios directly */
|
|
|
|
.direct_io = {
|
|
|
|
[0] = {.port = NULL},
|
|
|
|
[1] = {.port = NULL},
|
|
|
|
[2] = {.port = NULL},
|
|
|
|
[3] = {.port = NULL},
|
|
|
|
},
|
|
|
|
.direct_map = {
|
|
|
|
/* to be fixed */
|
|
|
|
9, 10, 11, 12
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
void initTle8888(DECLARE_ENGINE_PARAMETER_SIGNATURE)
|
|
|
|
{
|
2019-04-04 16:56:03 -07:00
|
|
|
if (engineConfiguration->tle8888_cs == GPIO_UNASSIGNED) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo: reuse initSpiCs method?
|
|
|
|
tle8888_cfg.spi_config.ssport = getHwPort("tle8888", engineConfiguration->tle8888_cs);
|
|
|
|
tle8888_cfg.spi_config.sspad = getHwPin("tle8888", engineConfiguration->tle8888_cs);
|
|
|
|
|
2019-04-04 16:29:33 -07:00
|
|
|
tle8888_cfg.spi_bus = getSpiDevice(engineConfiguration->tle8888spiDevice);
|
2019-04-04 16:56:03 -07:00
|
|
|
if (tle8888_cfg.spi_bus == NULL) {
|
|
|
|
// error already reported
|
|
|
|
return;
|
|
|
|
}
|
2019-04-04 16:29:33 -07:00
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
tle8888_add(0, &tle8888_cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********TO BE REMOVED FROM THIS FILE ENDS***********/
|
2019-04-04 07:08:50 -07:00
|
|
|
|
|
|
|
#endif /* EFI_TLE8888 */
|