2019-04-04 06:55:18 -07:00
|
|
|
/*
|
|
|
|
* tle8888.c
|
|
|
|
*
|
|
|
|
* TLE8888 Engine Machine System IC driver
|
|
|
|
*
|
2019-04-06 09:26:37 -07:00
|
|
|
* This has worked on a bench - see https://youtu.be/yjs5dh_NKo4
|
|
|
|
* All SPI and CS pin in OM_DEFAULT mode
|
|
|
|
*
|
2019-04-04 06:55:18 -07:00
|
|
|
* @date Mar 25, 2019
|
2020-01-07 21:02:40 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
2019-04-04 06:55:18 -07:00
|
|
|
*
|
2019-05-30 14:43:40 -07:00
|
|
|
* 3.2 Pin Definitions and Functions
|
|
|
|
*
|
|
|
|
* IN1-4 Parallel input; Input pin for direct control of power stage OUT1-4
|
|
|
|
*
|
|
|
|
* IN5-8 Parallel input; Input pin for direct control of push pull state IGN1-IGN4
|
|
|
|
*
|
|
|
|
* Table 24 Direct Drive Input Assignment to Output Stages
|
|
|
|
* IN9 to IN12 OUT5 to OUT24
|
|
|
|
*
|
|
|
|
* Masks/inputs bits:
|
|
|
|
* 0..3 - OUT1 .. 4 - INJ - OpenDrain: 2.2A - direct
|
|
|
|
* 4..6 - OUT5 .. 7 - OpenDrain: 4.5A
|
|
|
|
* 7..12 - OUT8 ..13 - Push/Pull: 20mA - quite weak
|
|
|
|
* 13..19 - OUT14..20 - OpenDrain: 0.6A
|
|
|
|
* 20..23 - OUT21..24 - Push/Pull: 0.6A - half bridge (GND or +12v)
|
|
|
|
* 24..27 - IGN1 .. 4 - Push/Pull: 20mA - direct
|
|
|
|
*
|
2019-04-04 06:55:18 -07:00
|
|
|
* Andrey Gusakov, (c) 2019
|
|
|
|
*/
|
|
|
|
|
2019-04-04 07:08:50 -07:00
|
|
|
#include "global.h"
|
|
|
|
|
2019-04-16 06:23:24 -07:00
|
|
|
#include "gpio/tle8888.h"
|
2019-04-04 07:08:50 -07:00
|
|
|
|
2019-04-16 06:23:24 -07:00
|
|
|
#if (BOARD_TLE8888_COUNT > 0)
|
2019-04-04 16:29:33 -07:00
|
|
|
|
2020-02-26 23:11:20 -08:00
|
|
|
#include "persistent_configuration.h"
|
2019-04-04 16:29:33 -07:00
|
|
|
#include "hardware.h"
|
2019-04-04 06:55:18 -07:00
|
|
|
#include "gpio/gpio_ext.h"
|
|
|
|
#include "pin_repository.h"
|
2019-07-06 17:15:49 -07:00
|
|
|
#include "os_util.h"
|
2019-04-13 07:58:52 -07:00
|
|
|
|
2020-02-26 23:11:20 -08:00
|
|
|
EXTERN_ENGINE_CONFIGURATION;
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/*
|
|
|
|
* TODO list:
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver local definitions. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
#define DRIVER_NAME "tle8888"
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
static bool drv_task_ready = false;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
TLE8888_DISABLED = 0,
|
|
|
|
TLE8888_WAIT_INIT,
|
|
|
|
TLE8888_READY,
|
|
|
|
TLE8888_FAILED
|
|
|
|
} tle8888_drv_state;
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2020-04-23 13:57:37 -07:00
|
|
|
// tle8888_mode_e
|
2019-09-05 07:01:59 -07:00
|
|
|
#define MODE_MANUAL 0x02
|
|
|
|
#define MODE_HALL 0x03
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/* 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))
|
2019-09-05 05:55:17 -07:00
|
|
|
#define CMD_R(a) (CMD_READ | CMD_REG_ADDR(a))
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2020-02-11 12:36:53 -08:00
|
|
|
/* Window watchdog open WWDOWT window time = 12.8 mS - fixed value for TLE8888QK */
|
|
|
|
#define CMD_WWDServiceCmd CMD_WR(0x15, 0x03)
|
2020-02-11 12:49:17 -08:00
|
|
|
#define FWDRespCmd(d) CMD_WR(0x16, d)
|
|
|
|
#define FWDRespSyncCmd(d) CMD_WR(0x17, d)
|
2020-02-10 08:11:00 -08:00
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
#define CMD_SR CMD_WR(0x1a, 0x03)
|
2019-04-06 07:30:20 -07:00
|
|
|
// 0x238 = 568
|
2019-04-04 06:55:18 -07:00
|
|
|
#define CMD_OE_SET CMD_WR(0x1c, 0x02)
|
2019-04-06 07:30:20 -07:00
|
|
|
/* not used
|
2019-04-04 06:55:18 -07:00
|
|
|
#define CMD_OE_CLR CMD_WR(0x1c, 0x01)
|
|
|
|
#define CMD_LOCK CMD_WR(0x1e, 0x02)
|
2019-04-06 07:30:20 -07:00
|
|
|
*/
|
2019-04-04 06:55:18 -07:00
|
|
|
#define CMD_UNLOCK CMD_WR(0x1e, 0x01)
|
2019-09-05 05:55:17 -07:00
|
|
|
|
2020-02-12 14:37:45 -08:00
|
|
|
#define WWDStat 0x36
|
|
|
|
#define FWDStat0 0x37
|
|
|
|
#define FWDStat1 0x38
|
|
|
|
|
2019-09-05 05:55:17 -07:00
|
|
|
/* Status registers */
|
|
|
|
#define CMD_OPSTAT0 CMD_R(0x34)
|
|
|
|
#define CMD_OPSTAT1 CMD_R(0x35)
|
2020-02-12 14:37:45 -08:00
|
|
|
#define CMD_WWDSTAT CMD_R(WWDStat)
|
|
|
|
#define CMD_FWDSTAT0 CMD_R(FWDStat0)
|
|
|
|
#define CMD_FWDSTAT1 CMD_R(FWDStat1)
|
2020-02-11 13:56:52 -08:00
|
|
|
#define CMD_TECSTAT CMD_R(0x39)
|
2020-02-10 18:28:55 -08:00
|
|
|
#define CMD_WdDiag CMD_R(0x2e)
|
2019-09-05 05:55:17 -07:00
|
|
|
|
2020-02-10 15:32:32 -08:00
|
|
|
|
2020-02-12 05:53:35 -08:00
|
|
|
#define CMD_OUTCONFIG(n, d) CMD_WR(0x40 + (n), d)
|
2019-09-04 17:02:32 -07:00
|
|
|
//#define CMD_VRSCONFIG0(d) CMD_WR(0x49, d)
|
2019-09-04 06:24:01 -07:00
|
|
|
#define CMD_VRSCONFIG1(d) CMD_WR(0x4a, d)
|
2019-04-04 06:55:18 -07:00
|
|
|
#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)
|
2019-04-12 16:22:16 -07:00
|
|
|
#define CMD_CONT(n, d) CMD_WR(0x7b + (n & 0x03), d)
|
2020-02-10 15:32:32 -08:00
|
|
|
|
|
|
|
const uint8_t watchDogResponses[16][4] = {
|
2020-02-11 12:36:53 -08:00
|
|
|
/* Reverse order:
|
|
|
|
* RESP3,RESP2,RESP1,REST0 */
|
2020-02-10 15:32:32 -08:00
|
|
|
{0xFF, 0x0F, 0xF0, 0x00},
|
|
|
|
{0xB0, 0x40, 0xBF, 0x4F},
|
|
|
|
{0xE9, 0x19, 0xE6, 0x16},
|
|
|
|
{0xA6, 0x56, 0xA9, 0x59},
|
|
|
|
{0x75, 0x85, 0x7A, 0x8A},
|
|
|
|
{0x3A, 0xCA, 0x35, 0xC5},
|
|
|
|
{0x63, 0x93, 0x6C, 0x9C},
|
|
|
|
{0x2C, 0xDC, 0x23, 0xD3},
|
|
|
|
{0xD2, 0x22, 0xDD, 0x2D},
|
|
|
|
{0x9D, 0x6D, 0x92, 0x62},
|
|
|
|
{0xC4, 0x34, 0xCB, 0x3B},
|
|
|
|
{0x8B, 0x7B, 0x84, 0x74},
|
|
|
|
{0x58, 0xA8, 0x57, 0xA7},
|
|
|
|
{0x17, 0xE7, 0x18, 0xE8},
|
|
|
|
{0x4E, 0xBE, 0x41, 0xB1},
|
|
|
|
{0x01, 0xF1, 0x0E, 0xFE}
|
|
|
|
};
|
2019-04-12 16:22:16 -07:00
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver exported variables. */
|
|
|
|
/*==========================================================================*/
|
2019-04-04 06:55:18 -07:00
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver local variables and types. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
/* OS */
|
|
|
|
SEMAPHORE_DECL(tle8888_wake, 10 /* or BOARD_TLE8888_COUNT ? */);
|
|
|
|
static THD_WORKING_AREA(tle8888_thread_1_wa, 256);
|
|
|
|
|
2020-02-10 10:24:09 -08:00
|
|
|
// todo: much of state is currently global while technically it should be per-chip. but we
|
|
|
|
// are lazy and in reality it's usually one chip per board
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For the timing check the microcontroller has to send periodically the window watchdog service command
|
|
|
|
* WWDServiceCmd. The window watchdog is triggered correctly if the command is received inside the open
|
|
|
|
* window of the window watchdog sequence.
|
|
|
|
*/
|
|
|
|
static efitick_t lastWindowWatchdogTimeNt = 0;
|
|
|
|
|
2020-02-10 11:37:52 -08:00
|
|
|
static efitick_t lastFunctionWatchdogTimeNt = 0;
|
|
|
|
|
2020-02-11 13:56:52 -08:00
|
|
|
static uint16_t WindowWatchdogErrorCounterValue;
|
|
|
|
static uint16_t FunctionalWatchdogPassCounterValue;
|
|
|
|
static uint16_t TotalErrorCounterValue;
|
|
|
|
|
2020-02-10 14:08:37 -08:00
|
|
|
static uint16_t maybeFirstResponse = 0;
|
2020-02-10 11:37:52 -08:00
|
|
|
static uint16_t functionWDrx = 0;
|
2020-02-10 18:28:55 -08:00
|
|
|
static uint16_t wdDiagResponse = 0;
|
2020-02-10 11:37:52 -08:00
|
|
|
|
2020-02-10 10:24:09 -08:00
|
|
|
//static_assert(TLE8888_POLL_INTERVAL_MS < Window_watchdog_open_window_time_ms)
|
|
|
|
|
2020-02-09 17:41:25 -08:00
|
|
|
static bool needInitialSpi = true;
|
2020-02-12 14:37:45 -08:00
|
|
|
static bool isWatchdogHappy = false;
|
|
|
|
static bool wasWatchdogHappy = false;
|
2020-02-26 23:11:20 -08:00
|
|
|
|
|
|
|
static int selfResetCounter = 0;
|
|
|
|
static int lowVoltageResetCounter = 0;
|
|
|
|
static int requestedResetCounter = 0;
|
|
|
|
int tle8888reinitializationCounter = 0;
|
|
|
|
|
2020-02-10 14:08:37 -08:00
|
|
|
float vBattForTle8888 = 0;
|
2020-02-09 17:41:25 -08:00
|
|
|
|
2019-09-05 07:01:59 -07:00
|
|
|
// set debug_mode 31
|
2019-06-05 18:44:58 -07:00
|
|
|
static int tle8888SpiCounter = 0;
|
2019-09-05 07:01:59 -07:00
|
|
|
// that's a strange variable for troubleshooting
|
2020-02-09 17:41:25 -08:00
|
|
|
int tle8888initResponsesAccumulator = 0;
|
2019-06-08 13:13:24 -07:00
|
|
|
static int initResponse0 = 0;
|
|
|
|
static int initResponse1 = 0;
|
2019-10-31 13:06:34 -07:00
|
|
|
static uint16_t spiRxb = 0, spiTxb = 0;
|
2019-06-08 13:13:24 -07:00
|
|
|
|
2019-06-05 18:44:58 -07:00
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/* Driver private data */
|
|
|
|
struct tle8888_priv {
|
|
|
|
const struct tle8888_config *cfg;
|
2019-04-12 16:22:16 -07:00
|
|
|
/* cached output state - state last send to chip */
|
|
|
|
uint32_t o_state_cached;
|
2020-02-09 17:41:25 -08:00
|
|
|
/* state to be sent to chip */
|
2019-04-12 16:22:16 -07:00
|
|
|
uint32_t o_state;
|
|
|
|
/* direct driven output mask */
|
|
|
|
uint32_t o_direct_mask;
|
|
|
|
/* output enabled mask */
|
|
|
|
uint32_t o_oe_mask;
|
|
|
|
|
|
|
|
tle8888_drv_state drv_state;
|
2019-09-05 05:55:17 -07:00
|
|
|
|
|
|
|
/* status registers */
|
|
|
|
uint16_t OpStat[2];
|
2019-04-04 06:55:18 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct tle8888_priv chips[BOARD_TLE8888_COUNT];
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
static const char* tle8888_pin_names[TLE8888_OUTPUTS] = {
|
|
|
|
"TLE8888.INJ1", "TLE8888.INJ2", "TLE8888.INJ3", "TLE8888.INJ4",
|
|
|
|
"TLE8888.OUT5", "TLE8888.OUT6", "TLE8888.OUT7", "TLE8888.OUT8",
|
|
|
|
"TLE8888.OUT9", "TLE8888.OUT10", "TLE8888.OUT11", "TLE8888.OUT12",
|
|
|
|
"TLE8888.OUT13", "TLE8888.OUT14", "TLE8888.OUT15", "TLE8888.OUT16",
|
|
|
|
"TLE8888.OUT17", "TLE8888.OUT18", "TLE8888.OUT19", "TLE8888.OUT20",
|
2019-04-21 05:50:07 -07:00
|
|
|
"TLE8888.OUT21", "TLE8888.OUT22", "TLE8888.OUT23", "TLE8888.OUT24",
|
2019-04-12 16:22:16 -07:00
|
|
|
"TLE8888.IGN1", "TLE8888.IGN2", "TLE8888.IGN3", "TLE8888.IGN4"
|
|
|
|
};
|
|
|
|
|
2020-02-12 14:37:45 -08:00
|
|
|
#define getWindowWatchdog() ((WindowWatchdogErrorCounterValue >> 8) & 0x3f)
|
|
|
|
#define getFunctionalWatchdog() ((FunctionalWatchdogPassCounterValue >> 8) & 0x3f)
|
|
|
|
#define getTotalErrorCounter() ((TotalErrorCounterValue >> 8) & 0x3f)
|
|
|
|
|
2019-10-31 13:06:34 -07:00
|
|
|
#if EFI_TUNER_STUDIO
|
2020-02-26 23:11:20 -08:00
|
|
|
// set debug_mode 31
|
2019-12-22 05:12:45 -08:00
|
|
|
void tle8888PostState(TsDebugChannels *debugChannels) {
|
2020-02-11 13:56:52 -08:00
|
|
|
|
2020-02-12 14:37:45 -08:00
|
|
|
debugChannels->debugIntField1 = getWindowWatchdog();
|
|
|
|
debugChannels->debugIntField2 = getFunctionalWatchdog();
|
|
|
|
debugChannels->debugIntField3 = getTotalErrorCounter();
|
2020-02-11 13:56:52 -08:00
|
|
|
//debugChannels->debugIntField1 = tle8888SpiCounter;
|
|
|
|
//debugChannels->debugIntField2 = spiTxb;
|
|
|
|
//debugChannels->debugIntField3 = spiRxb;
|
2020-02-09 17:41:25 -08:00
|
|
|
debugChannels->debugIntField4 = tle8888initResponsesAccumulator;
|
|
|
|
debugChannels->debugIntField5 = tle8888reinitializationCounter;
|
2019-12-22 05:12:45 -08:00
|
|
|
debugChannels->debugFloatField1 = initResponse0;
|
|
|
|
debugChannels->debugFloatField2 = initResponse1;
|
2020-02-10 08:11:00 -08:00
|
|
|
|
|
|
|
debugChannels->debugFloatField3 = chips[0].OpStat[1];
|
2020-02-26 23:11:20 -08:00
|
|
|
debugChannels->debugFloatField4 = selfResetCounter * 1000000 + requestedResetCounter * 10000 + lowVoltageResetCounter;
|
2020-02-10 11:37:52 -08:00
|
|
|
debugChannels->debugFloatField5 = functionWDrx;
|
2020-02-10 18:28:55 -08:00
|
|
|
debugChannels->debugFloatField6 = wdDiagResponse;
|
2019-10-31 13:06:34 -07:00
|
|
|
}
|
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver local functions. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
static SPIDriver *get_bus(struct tle8888_priv *chip)
|
|
|
|
{
|
|
|
|
/* return non-const SPIDriver* from const struct cfg */
|
|
|
|
return chip->cfg->spi_bus;
|
|
|
|
}
|
|
|
|
|
2020-02-10 08:11:00 -08:00
|
|
|
/**
|
|
|
|
* @return always return 0 for now
|
|
|
|
*/
|
2019-04-04 06:55:18 -07:00
|
|
|
static int tle8888_spi_rw(struct tle8888_priv *chip, uint16_t tx, uint16_t *rx)
|
|
|
|
{
|
|
|
|
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-10-31 13:06:34 -07:00
|
|
|
spiRxb = spiPolledExchange(spi, tx);
|
2019-04-06 04:38:02 -07:00
|
|
|
//spiExchange(spi, 2, &tx, &rxb); 8 bit version just in case?
|
2019-04-04 06:55:18 -07:00
|
|
|
/* Slave Select de-assertion. */
|
|
|
|
spiUnselect(spi);
|
|
|
|
/* Ownership release. */
|
|
|
|
spiReleaseBus(spi);
|
|
|
|
|
2019-10-31 13:06:34 -07:00
|
|
|
spiTxb = tx;
|
|
|
|
tle8888SpiCounter++;
|
2019-04-04 20:03:32 -07:00
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
if (rx)
|
2019-10-31 13:06:34 -07:00
|
|
|
*rx = spiRxb;
|
2019-04-04 06:55:18 -07:00
|
|
|
|
|
|
|
/* no errors for now */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
/**
|
|
|
|
* @brief TLE8888 send output registers data.
|
2019-09-05 05:55:17 -07:00
|
|
|
* @details Sends ORed data to register.
|
2019-04-12 16:22:16 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int tle8888_update_output(struct tle8888_priv *chip)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* TODO: lock? */
|
|
|
|
|
|
|
|
/* set value only for non-direct driven pins */
|
2019-06-05 18:44:58 -07:00
|
|
|
uint32_t out_data = chip->o_state & (~chip->o_direct_mask);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
2019-04-12 16:22:16 -07:00
|
|
|
uint8_t od;
|
|
|
|
|
|
|
|
od = (out_data >> (8 * i)) & 0xff;
|
|
|
|
ret |= tle8888_spi_rw(chip, CMD_CONT(i, od), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
/* atomic */
|
|
|
|
chip->o_state_cached = out_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: unlock? */
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-09-05 05:55:17 -07:00
|
|
|
/**
|
|
|
|
* @brief read TLE8888 OpStat1 registers data.
|
|
|
|
* @details Sends read command, then send same command and read reply
|
|
|
|
*/
|
|
|
|
static int tle8888_update_status(struct tle8888_priv *chip)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
uint16_t rx = 0;
|
|
|
|
|
|
|
|
/* TODO: lock? */
|
|
|
|
|
2020-02-10 11:37:52 -08:00
|
|
|
// todo: extract helper method?
|
2019-09-05 05:55:17 -07:00
|
|
|
/* the address and content of the selected register is transmitted with the
|
|
|
|
* next SPI transmission (for not existing addresses or wrong access mode
|
2019-09-05 07:01:59 -07:00
|
|
|
* the data is always '0' */
|
2019-09-05 05:55:17 -07:00
|
|
|
|
|
|
|
ret = tle8888_spi_rw(chip, CMD_OPSTAT1, NULL);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = tle8888_spi_rw(chip, CMD_OPSTAT1, &rx);
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-02-10 08:11:00 -08:00
|
|
|
// available in debugFloatField3
|
2019-09-05 05:55:17 -07:00
|
|
|
chip->OpStat[1] = rx;
|
|
|
|
|
|
|
|
/* TODO: unlock? */
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
static int tle8888_update_direct_output(struct tle8888_priv *chip, int pin, int value)
|
|
|
|
{
|
|
|
|
const struct tle8888_config *cfg = chip->cfg;
|
|
|
|
|
|
|
|
/* find direct drive gpio */
|
2019-06-05 18:44:58 -07:00
|
|
|
for (int i = 0; i < TLE8888_DIRECT_MISC; i++) {
|
2019-04-12 16:22:16 -07:00
|
|
|
/* again: outputs in cfg counted starting from 1 - hate this */
|
|
|
|
if (cfg->direct_io[i].output == pin + 1) {
|
|
|
|
if (value)
|
|
|
|
palSetPort(cfg->direct_io[i].port,
|
|
|
|
PAL_PORT_BIT(cfg->direct_io[i].pad));
|
|
|
|
else
|
|
|
|
palClearPort(cfg->direct_io[i].port,
|
|
|
|
PAL_PORT_BIT(cfg->direct_io[i].pad));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* direct gpio not found */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-08-09 21:53:16 -07:00
|
|
|
// ChibiOS does not offer this function so that's a copy-paste of 'chSemSignal' without locking
|
|
|
|
void chSemSignalS(semaphore_t *sp) {
|
|
|
|
|
|
|
|
chDbgCheck(sp != NULL);
|
|
|
|
|
|
|
|
chDbgAssert(((sp->cnt >= (cnt_t)0) && queue_isempty(&sp->queue)) ||
|
|
|
|
((sp->cnt < (cnt_t)0) && queue_notempty(&sp->queue)),
|
|
|
|
"inconsistent semaphore");
|
|
|
|
if (++sp->cnt <= (cnt_t)0) {
|
|
|
|
chSchWakeupS(queue_fifo_remove(&sp->queue), MSG_OK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
/**
|
|
|
|
* @brief TLE8888 chip driver wakeup.
|
|
|
|
* @details Wake up driver. Will cause output register update
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int tle8888_wake_driver(struct tle8888_priv *chip)
|
|
|
|
{
|
|
|
|
(void)chip;
|
|
|
|
|
2019-08-11 12:23:15 -07:00
|
|
|
/* Entering a reentrant critical zone.*/
|
|
|
|
syssts_t sts = chSysGetStatusAndLockX();
|
2019-08-16 19:01:32 -07:00
|
|
|
chSemSignalI(&tle8888_wake);
|
2020-02-09 18:16:31 -08:00
|
|
|
if (!port_is_isr_context()) {
|
|
|
|
/**
|
|
|
|
* chSemSignalI above requires rescheduling
|
|
|
|
* interrupt handlers have implicit rescheduling
|
|
|
|
*/
|
2020-02-11 14:52:01 -08:00
|
|
|
chSchRescheduleS();
|
2020-02-09 18:16:31 -08:00
|
|
|
}
|
2019-08-11 12:23:15 -07:00
|
|
|
/* Leaving the critical zone.*/
|
|
|
|
chSysRestoreStatusX(sts);
|
2019-04-12 16:22:16 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-04-04 06:55:18 -07:00
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver thread. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
2020-02-10 15:32:32 -08:00
|
|
|
static void handleFWDStat1(struct tle8888_priv *chip, int registerNum, int data) {
|
|
|
|
if (registerNum != FWDStat1)
|
|
|
|
return;
|
|
|
|
uint8_t FWDQUEST = data & 0xF;
|
|
|
|
uint8_t FWDRESPC = (data >> 4) & 3;
|
2020-02-11 12:36:53 -08:00
|
|
|
/* Table lines are filled in reverse order (like in DS) */
|
|
|
|
uint8_t response = watchDogResponses[FWDQUEST][3 - FWDRESPC];
|
2020-02-11 12:49:17 -08:00
|
|
|
if (FWDRESPC) {
|
|
|
|
tle8888_spi_rw(chip, FWDRespCmd(response), NULL);
|
|
|
|
} else {
|
|
|
|
/* to restart heartbeat timer, sync command should be used for response 0 */
|
|
|
|
tle8888_spi_rw(chip, FWDRespSyncCmd(response), NULL);
|
|
|
|
}
|
2020-02-10 18:28:55 -08:00
|
|
|
tle8888_spi_rw(chip, CMD_WdDiag, NULL);
|
|
|
|
tle8888_spi_rw(chip, CMD_WdDiag, &wdDiagResponse);
|
2020-02-10 15:32:32 -08:00
|
|
|
}
|
|
|
|
|
2020-02-12 16:32:40 -08:00
|
|
|
int startupConfiguration(struct tle8888_priv *chip) {
|
|
|
|
const struct tle8888_config *cfg = chip->cfg;
|
|
|
|
uint16_t response = 0;
|
|
|
|
/* Set LOCK bit to 0 */
|
|
|
|
// second 0x13D=317 => 0x35=53
|
|
|
|
tle8888_spi_rw(chip, CMD_UNLOCK, &response);
|
|
|
|
if (response == 53) {
|
|
|
|
tle8888initResponsesAccumulator += 8;
|
|
|
|
}
|
|
|
|
initResponse1 = response;
|
|
|
|
|
|
|
|
chip->o_direct_mask = 0;
|
|
|
|
chip->o_oe_mask = 0;
|
|
|
|
/* enable direct drive of OUTPUT4..1
|
|
|
|
* ...still need INJEN signal */
|
|
|
|
chip->o_direct_mask |= 0x0000000f;
|
|
|
|
chip->o_oe_mask |= 0x0000000f;
|
|
|
|
/* enable direct drive of IGN4..1
|
|
|
|
* ...still need IGNEN signal */
|
|
|
|
chip->o_direct_mask |= 0x0f000000;
|
|
|
|
chip->o_oe_mask |= 0x0f000000;
|
|
|
|
|
|
|
|
/* map and enable outputs for direct driven channels */
|
|
|
|
for (int i = 0; i < TLE8888_DIRECT_MISC; i++) {
|
|
|
|
int out = cfg->direct_io[i].output;
|
|
|
|
|
|
|
|
/* not used? */
|
|
|
|
if (out == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* OUT1..4 driven direct only through dedicated pins */
|
|
|
|
if (out < 5)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* in config counted from 1 */
|
|
|
|
uint32_t mask = (1 << (out - 1));
|
|
|
|
|
|
|
|
/* check if output already occupied */
|
|
|
|
if (chip->o_direct_mask & mask) {
|
|
|
|
/* incorrect config? */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable direct drive and output enable */
|
|
|
|
chip->o_direct_mask |= mask;
|
|
|
|
chip->o_oe_mask |= mask;
|
|
|
|
|
|
|
|
/* set INCONFIG - aux input mapping */
|
|
|
|
tle8888_spi_rw(chip, CMD_INCONFIG(i, out - 5), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable all ouputs
|
|
|
|
* TODO: add API to enable/disable? */
|
|
|
|
chip->o_oe_mask |= 0x0ffffff0;
|
|
|
|
|
|
|
|
/* set OE and DD registers */
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
uint8_t oe, dd;
|
|
|
|
|
|
|
|
oe = (chip->o_oe_mask >> (8 * i)) & 0xff;
|
|
|
|
dd = (chip->o_direct_mask >> (8 * i)) & 0xff;
|
|
|
|
tle8888_spi_rw(chip, CMD_OECONFIG(i, oe), NULL);
|
|
|
|
tle8888_spi_rw(chip, CMD_DDCONFIG(i, dd), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Debug: disable diagnostic */
|
|
|
|
for (int i = 0; i <= 5; i++) {
|
|
|
|
tle8888_spi_rw(chip, CMD_OUTCONFIG(i, 0), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable outputs */
|
|
|
|
tle8888_spi_rw(chip, CMD_OE_SET, NULL);
|
|
|
|
|
2020-04-23 13:57:37 -07:00
|
|
|
if (cfg->mode > 0) {
|
|
|
|
tle8888_spi_rw(chip, CMD_VRSCONFIG1(cfg->mode << 2), NULL);
|
2020-02-12 16:32:40 -08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-10 14:08:37 -08:00
|
|
|
void watchdogLogic(struct tle8888_priv *chip) {
|
|
|
|
efitick_t nowNt = getTimeNowNt();
|
|
|
|
if (nowNt - lastWindowWatchdogTimeNt > MS2NT(Window_watchdog_close_window_time_ms)) {
|
|
|
|
tle8888_spi_rw(chip, CMD_WWDServiceCmd, &maybeFirstResponse);
|
|
|
|
lastWindowWatchdogTimeNt = nowNt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nowNt - lastFunctionWatchdogTimeNt > MS2NT(Functional_Watchdog_PERIOD_MS)) {
|
|
|
|
// todo: extract helper method?
|
|
|
|
/* 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' */
|
2020-02-12 14:37:45 -08:00
|
|
|
tle8888_spi_rw(chip, CMD_FWDSTAT1, &maybeFirstResponse);
|
2020-02-10 21:54:15 -08:00
|
|
|
// here we get response of the 'FWDStat1' above
|
|
|
|
tle8888_spi_rw(chip, CMD_WdDiag, &functionWDrx);
|
2020-02-12 14:37:45 -08:00
|
|
|
handleFWDStat1(chip, getRegisterFromResponse(functionWDrx), (functionWDrx >> 8) & 0xff);
|
2020-02-10 14:08:37 -08:00
|
|
|
lastFunctionWatchdogTimeNt = nowNt;
|
|
|
|
}
|
|
|
|
|
2020-02-11 13:56:52 -08:00
|
|
|
tle8888_spi_rw(chip, CMD_WWDSTAT, NULL);
|
2020-02-12 14:37:45 -08:00
|
|
|
tle8888_spi_rw(chip, CMD_FWDSTAT0, &WindowWatchdogErrorCounterValue);
|
2020-02-11 13:56:52 -08:00
|
|
|
tle8888_spi_rw(chip, CMD_TECSTAT, &FunctionalWatchdogPassCounterValue);
|
|
|
|
tle8888_spi_rw(chip, CMD_TECSTAT, &TotalErrorCounterValue);
|
2020-02-12 14:37:45 -08:00
|
|
|
|
|
|
|
|
|
|
|
// sanity checking that we are looking at the right responses
|
|
|
|
if (getRegisterFromResponse(WindowWatchdogErrorCounterValue) == WWDStat &&
|
|
|
|
getRegisterFromResponse(FunctionalWatchdogPassCounterValue) == FWDStat0
|
|
|
|
) {
|
|
|
|
|
|
|
|
wasWatchdogHappy = isWatchdogHappy;
|
|
|
|
// reset state for error counters has us start in Safe Mode
|
2020-02-12 16:32:40 -08:00
|
|
|
isWatchdogHappy = (getWindowWatchdog() == 0 && getFunctionalWatchdog() == 0);
|
|
|
|
if (!wasWatchdogHappy && isWatchdogHappy) {
|
|
|
|
startupConfiguration(chip);
|
|
|
|
}
|
2020-02-12 14:37:45 -08:00
|
|
|
}
|
2020-02-10 14:08:37 -08:00
|
|
|
}
|
|
|
|
|
2020-02-09 17:41:25 -08:00
|
|
|
int tle8888SpiStartupExchange(struct tle8888_priv *chip);
|
2019-04-12 16:22:16 -07:00
|
|
|
|
2019-08-16 19:56:06 -07:00
|
|
|
static THD_FUNCTION(tle8888_driver_thread, p) {
|
2019-04-12 16:22:16 -07:00
|
|
|
(void)p;
|
|
|
|
|
|
|
|
chRegSetThreadName(DRIVER_NAME);
|
|
|
|
|
2019-06-05 18:44:58 -07:00
|
|
|
while (1) {
|
2019-08-16 19:56:06 -07:00
|
|
|
msg_t msg = chSemWaitTimeout(&tle8888_wake, TIME_MS2I(TLE8888_POLL_INTERVAL_MS));
|
|
|
|
|
2019-09-05 05:55:17 -07:00
|
|
|
/* should we care about msg == MSG_TIMEOUT? */
|
|
|
|
(void)msg;
|
|
|
|
|
2019-08-16 19:56:06 -07:00
|
|
|
if (vBattForTle8888 < 7) {
|
|
|
|
// we assume TLE8888 is down and we should not bother with SPI communication
|
2020-02-26 23:11:20 -08:00
|
|
|
if (!needInitialSpi) {
|
|
|
|
needInitialSpi = true;
|
|
|
|
lowVoltageResetCounter++;
|
|
|
|
}
|
2020-02-10 14:08:37 -08:00
|
|
|
continue; // we should not bother communicating with TLE8888 until we have +12
|
2019-08-16 19:56:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (needInitialSpi) {
|
2020-02-12 16:32:40 -08:00
|
|
|
wasWatchdogHappy = isWatchdogHappy = needInitialSpi = false;
|
2020-02-09 17:41:25 -08:00
|
|
|
|
|
|
|
for (int i = 0; i < BOARD_TLE8888_COUNT; i++) {
|
|
|
|
struct tle8888_priv *chip = &chips[i];
|
|
|
|
tle8888SpiStartupExchange(chip);
|
|
|
|
}
|
2019-08-16 19:56:06 -07:00
|
|
|
}
|
|
|
|
|
2020-02-10 14:08:37 -08:00
|
|
|
// todo: super-lazy implementation with only first chip!
|
|
|
|
watchdogLogic(&chips[0]);
|
2020-02-10 10:24:09 -08:00
|
|
|
|
2019-06-05 18:44:58 -07:00
|
|
|
for (int i = 0; i < BOARD_TLE8888_COUNT; i++) {
|
2020-02-09 17:41:25 -08:00
|
|
|
struct tle8888_priv *chip = &chips[i];
|
2019-04-12 16:22:16 -07:00
|
|
|
if ((chip->cfg == NULL) ||
|
|
|
|
(chip->drv_state == TLE8888_DISABLED) ||
|
|
|
|
(chip->drv_state == TLE8888_FAILED))
|
|
|
|
continue;
|
|
|
|
|
2019-06-05 18:44:58 -07:00
|
|
|
int ret = tle8888_update_output(chip);
|
2019-04-12 16:22:16 -07:00
|
|
|
if (ret) {
|
|
|
|
/* set state to TLE8888_FAILED? */
|
|
|
|
}
|
2019-09-05 05:55:17 -07:00
|
|
|
|
|
|
|
ret = tle8888_update_status(chip);
|
|
|
|
if (ret) {
|
|
|
|
/* set state to TLE8888_FAILED or force reinit? */
|
|
|
|
}
|
|
|
|
|
2019-09-05 07:30:27 -07:00
|
|
|
/* if bit OE is cleared - reset happened */
|
2019-09-05 05:55:17 -07:00
|
|
|
if (!(chip->OpStat[1] & (1 << 6))) {
|
|
|
|
needInitialSpi = true;
|
2020-02-26 23:11:20 -08:00
|
|
|
selfResetCounter++;
|
2019-09-05 05:55:17 -07:00
|
|
|
}
|
2019-04-12 16:22:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-05 07:30:27 -07:00
|
|
|
void requestTLE8888initialization(void) {
|
|
|
|
needInitialSpi = true;
|
2020-02-26 23:11:20 -08:00
|
|
|
requestedResetCounter++;
|
2019-09-05 07:30:27 -07:00
|
|
|
}
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver interrupt handlers. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
|
|
|
/*==========================================================================*/
|
|
|
|
/* Driver exported functions. */
|
|
|
|
/*==========================================================================*/
|
|
|
|
|
2020-01-11 07:29:41 -08:00
|
|
|
int tle8888_writePad(void *data, unsigned int pin, int value) {
|
2019-04-12 16:22:16 -07:00
|
|
|
|
2019-04-13 09:43:19 -07:00
|
|
|
if ((pin >= TLE8888_OUTPUTS) || (data == NULL))
|
2019-04-12 16:22:16 -07:00
|
|
|
return -1;
|
|
|
|
|
2019-08-10 08:13:41 -07:00
|
|
|
struct tle8888_priv *chip = (struct tle8888_priv *)data;
|
2019-04-12 16:22:16 -07:00
|
|
|
|
|
|
|
/* TODO: lock */
|
2019-08-10 08:13:41 -07:00
|
|
|
if (value) {
|
2019-04-12 16:22:16 -07:00
|
|
|
chip->o_state |= (1 << pin);
|
2019-08-10 08:13:41 -07:00
|
|
|
} else {
|
2019-04-12 16:22:16 -07:00
|
|
|
chip->o_state &= ~(1 << pin);
|
2019-08-10 08:13:41 -07:00
|
|
|
}
|
2019-04-12 16:22:16 -07:00
|
|
|
/* TODO: unlock */
|
|
|
|
/* direct driven? */
|
|
|
|
if (chip->o_direct_mask & (1 << pin)) {
|
|
|
|
return tle8888_update_direct_output(chip, pin, value);
|
|
|
|
} else {
|
|
|
|
return tle8888_wake_driver(chip);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-16 19:41:19 -07:00
|
|
|
/**
|
|
|
|
* @return 0 for valid configuration, -1 for invalid configuration
|
|
|
|
*/
|
2020-02-09 17:41:25 -08:00
|
|
|
int tle8888SpiStartupExchange(struct tle8888_priv *chip) {
|
|
|
|
tle8888reinitializationCounter++;
|
|
|
|
tle8888initResponsesAccumulator = 0;
|
2019-09-05 07:01:59 -07:00
|
|
|
|
2019-08-16 20:21:03 -07:00
|
|
|
/**
|
|
|
|
* We need around 50ms to get reliable TLE8888 start if MCU is powered externally but +12 goes gown and then goes up
|
|
|
|
* again
|
|
|
|
*/
|
|
|
|
chThdSleepMilliseconds(50);
|
2020-02-10 14:08:37 -08:00
|
|
|
|
|
|
|
watchdogLogic(chip);
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/* Software reset */
|
2019-06-05 19:32:30 -07:00
|
|
|
// first packet: 0x335=821 > 0xFD=253
|
2019-08-16 19:41:19 -07:00
|
|
|
uint16_t response = 0;
|
2019-06-05 19:32:30 -07:00
|
|
|
tle8888_spi_rw(chip, CMD_SR, &response);
|
|
|
|
if (response == 253) {
|
2019-06-08 18:58:23 -07:00
|
|
|
// I've seen this response on red board
|
2020-02-09 17:41:25 -08:00
|
|
|
tle8888initResponsesAccumulator += 4;
|
2019-11-19 15:17:03 -08:00
|
|
|
} else if (response == 2408) {
|
2019-06-08 18:58:23 -07:00
|
|
|
// and I've seen this response on red board
|
2020-02-09 17:41:25 -08:00
|
|
|
tle8888initResponsesAccumulator += 100;
|
2019-06-05 19:32:30 -07:00
|
|
|
}
|
2019-06-08 13:13:24 -07:00
|
|
|
initResponse0 = response;
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2019-04-06 07:30:20 -07:00
|
|
|
/**
|
|
|
|
* Table 8. Reset Times. All reset times not more than 20uS
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
chThdSleepMilliseconds(3);
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2020-02-12 16:32:40 -08:00
|
|
|
startupConfiguration(chip);
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2019-09-03 21:27:19 -07:00
|
|
|
|
2020-02-26 23:11:20 -08:00
|
|
|
if (CONFIG(verboseTLE8888)) {
|
|
|
|
tle8888_dump_regs();
|
|
|
|
}
|
2019-08-16 19:41:19 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-16 19:56:06 -07:00
|
|
|
static int tle8888_chip_init(void * data) {
|
2019-08-16 19:41:19 -07:00
|
|
|
struct tle8888_priv *chip = (struct tle8888_priv *)data;
|
|
|
|
const struct tle8888_config *cfg = chip->cfg;
|
|
|
|
|
|
|
|
int ret = 0;
|
|
|
|
/* mark pins used */
|
|
|
|
// we do not initialize CS pin so we should not be marking it used - i'm sad
|
|
|
|
//ret = gpio_pin_markUsed(cfg->spi_config.ssport, cfg->spi_config.sspad, DRIVER_NAME " CS");
|
|
|
|
if (cfg->reset.port != NULL)
|
|
|
|
ret |= gpio_pin_markUsed(cfg->reset.port, cfg->reset.pad, DRIVER_NAME " RST");
|
|
|
|
for (int i = 0; i < TLE8888_DIRECT_MISC; i++)
|
|
|
|
if (cfg->direct_io[i].port)
|
|
|
|
ret |= gpio_pin_markUsed(cfg->direct_io[i].port, cfg->direct_io[i].pad, DRIVER_NAME " DIRECT IO");
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
ret = -1;
|
|
|
|
goto err_gpios;
|
|
|
|
}
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
|
|
|
|
err_gpios:
|
|
|
|
/* unmark pins */
|
2019-04-09 16:31:10 -07:00
|
|
|
//gpio_pin_markUnused(cfg->spi_config.ssport, cfg->spi_config.sspad);
|
2019-04-04 06:55:18 -07:00
|
|
|
if (cfg->reset.port != NULL)
|
2019-04-09 16:31:10 -07:00
|
|
|
gpio_pin_markUnused(cfg->reset.port, cfg->reset.pad);
|
2019-06-05 18:44:58 -07:00
|
|
|
for (int i = 0; i < TLE8888_DIRECT_MISC; i++)
|
2019-04-04 06:55:18 -07:00
|
|
|
if (cfg->direct_io[i].port)
|
2019-04-09 16:31:10 -07:00
|
|
|
gpio_pin_markUnused(cfg->direct_io[i].port, cfg->direct_io[i].pad);
|
2019-04-05 15:37:00 -07:00
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-02-11 14:49:14 -08:00
|
|
|
/* DEBUG */
|
|
|
|
void tle8888_read_reg(uint16_t reg, uint16_t *val)
|
|
|
|
{
|
|
|
|
struct tle8888_priv *chip = &chips[0];
|
|
|
|
|
|
|
|
tle8888_spi_rw(chip, CMD_R(reg), val);
|
|
|
|
}
|
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
int tle8888_init(void * data)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct tle8888_priv *chip;
|
|
|
|
|
|
|
|
chip = (struct tle8888_priv *)data;
|
|
|
|
|
|
|
|
/* check for multiple init */
|
|
|
|
if (chip->drv_state != TLE8888_WAIT_INIT)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = tle8888_chip_init(chip);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
chip->drv_state = TLE8888_READY;
|
|
|
|
|
2019-04-14 05:15:01 -07:00
|
|
|
/* one task for all TLE8888 instances, so create only once */
|
2019-04-12 16:22:16 -07:00
|
|
|
if (!drv_task_ready) {
|
|
|
|
chThdCreateStatic(tle8888_thread_1_wa, sizeof(tle8888_thread_1_wa),
|
|
|
|
NORMALPRIO + 1, tle8888_driver_thread, NULL);
|
|
|
|
drv_task_ready = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int tle8888_deinit(void *data)
|
|
|
|
{
|
|
|
|
(void)data;
|
|
|
|
|
|
|
|
/* TODO: set all pins to inactive state, stop task? */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct gpiochip_ops tle8888_ops = {
|
|
|
|
.writePad = tle8888_writePad,
|
|
|
|
.readPad = NULL, /* chip outputs only */
|
|
|
|
//.getDiag = tle8888_getDiag,
|
|
|
|
.init = tle8888_init,
|
|
|
|
.deinit = tle8888_deinit,
|
|
|
|
};
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/**
|
|
|
|
* @brief TLE8888 driver add.
|
|
|
|
* @details Checks for valid config
|
2019-04-13 07:58:52 -07:00
|
|
|
* @return return gpio chip base
|
2019-04-04 06:55:18 -07:00
|
|
|
*/
|
|
|
|
|
2019-08-10 08:13:41 -07:00
|
|
|
int tle8888_add(unsigned int index, const struct tle8888_config *cfg) {
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
efiAssert(OBD_PCM_Processor_Fault, cfg != NULL, "8888CFG", 0)
|
|
|
|
|
2019-04-04 06:55:18 -07:00
|
|
|
/* no config or no such chip */
|
2019-04-18 22:42:24 -07:00
|
|
|
if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_TLE8888_COUNT))
|
|
|
|
return -1;
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2019-08-16 19:56:06 -07:00
|
|
|
/* check for valid chip select.
|
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;
|
|
|
|
|
2019-08-16 19:56:06 -07:00
|
|
|
struct tle8888_priv *chip = &chips[index];
|
2019-04-04 06:55:18 -07:00
|
|
|
|
|
|
|
/* already initted? */
|
|
|
|
if (chip->cfg != NULL)
|
|
|
|
return -1;
|
2019-04-12 16:22:16 -07:00
|
|
|
|
2019-04-04 20:03:32 -07:00
|
|
|
chip->cfg = cfg;
|
2019-04-12 16:22:16 -07:00
|
|
|
chip->o_state = 0;
|
|
|
|
chip->o_state_cached = 0;
|
|
|
|
chip->o_direct_mask = 0;
|
|
|
|
chip->drv_state = TLE8888_WAIT_INIT;
|
|
|
|
|
|
|
|
/* register, return gpio chip base */
|
2019-08-10 08:13:41 -07:00
|
|
|
int ret = gpiochip_register(DRIVER_NAME, &tle8888_ops, TLE8888_OUTPUTS, chip);
|
2019-04-12 16:22:16 -07:00
|
|
|
|
|
|
|
/* set default pin names, board init code can rewrite */
|
|
|
|
gpiochips_setPinNames(ret, tle8888_pin_names);
|
2019-04-04 06:55:18 -07:00
|
|
|
|
2019-04-12 16:22:16 -07:00
|
|
|
return ret;
|
2019-04-04 06:55:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#else /* BOARD_TLE8888_COUNT > 0 */
|
|
|
|
|
|
|
|
int tle8888_add(unsigned int index, const struct tle8888_config *cfg)
|
|
|
|
{
|
|
|
|
(void)index; (void)cfg;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-14 04:45:28 -07:00
|
|
|
#endif /* (BOARD_TLE8888_COUNT > 0) */
|