rusefi/firmware/hw_layer/drivers/gpio/tle9104.cpp

234 lines
4.6 KiB
C++

#include "pch.h"
#include "gpio/gpio_ext.h"
#include "gpio/tle9104.h"
#if BOARD_TLE9104_COUNT > 0
#define TLE9104_REG_CTRL 0x00
#define TLE9104_REG_CFG 0x01
#define TLE9104_REG_OFF_DIAG_CFG 0x02
#define TLE9104_REG_ON_DIAG_CFG 0x03
#define TLE9104_REG_DIAG_OUT_1_2_ON 0x04
#define TLE9104_REG_DIAG_OUT_3_4_ON 0x05
#define TLE9104_REG_DIAG_OFF 0x06
#define TLE9104_REG_GLOBAL_STATUS 0x07
#define TLE9104_REG_ICVID 0x08
struct Tle9104 : public GpioChip {
int init() override;
int writePad(size_t pin, int value) override;
brain_pin_diag_e getDiag(size_t pin) override;
uint16_t readWrite(uint8_t addr, uint8_t data);
uint16_t read(uint8_t addr);
void write(uint8_t addr, uint8_t data);
void updateDiagState();
const tle9104_config* cfg;
uint8_t diag_off;
uint8_t diag_on12;
uint8_t diag_on34;
OutputPin m_en;
OutputPin m_resn;
};
static bool parityBit(uint16_t val) {
// (1 + number of bits set) mod 2 = parity bit
int count = 1;
while (val != 0) {
if (val & 0x01) {
count++;
}
val = val >> 1;
}
return (count % 2) == 1;
}
uint16_t Tle9104::readWrite(uint8_t addr, uint8_t data) {
uint16_t tx = (addr << 8) + data;
// set the parity bit appropriately
tx |= parityBit(tx) ? (1 << 14) : 0;
SPIDriver* spi = cfg->spi_bus;
spiSelect(spi);
uint16_t rx = spiPolledExchange(spi, tx);
spiUnselect(spi);
/*bool parityOk = */parityBit(rx);
// return data
return rx;
}
uint16_t Tle9104::read(uint8_t addr) {
// R/W bit is 0 for read
readWrite(addr, 0);
return readWrite(addr, 0);
}
void Tle9104::write(uint8_t addr, uint8_t data) {
// R/W bit is 1 for write
readWrite(0x80 | addr, data);
}
int Tle9104::init() {
m_resn.initPin("TLE9104 RESN", cfg->resn);
m_en.initPin("TLE9104 EN", cfg->en);
// disable outputs
m_en.setValue(false);
for (int i = 0; i < 4; i++) {
gpio_pin_markUsed(cfg->direct_io[i].port, cfg->direct_io[i].pad, "TLE9104 Direct IO");
palSetPadMode(cfg->direct_io[i].port, cfg->direct_io[i].pad, PAL_MODE_OUTPUT_PUSHPULL);
// Ensure all outputs are off
writePad(i, false);
}
// Reset the chip
m_resn.setValue(false);
chThdSleepMilliseconds(1);
m_resn.setValue(true);
chThdSleepMilliseconds(1);
spiStart(cfg->spi_bus, &cfg->spi_config);
// read ID register
uint16_t id = read(TLE9104_REG_ICVID);
// No chip detected if ID is wrong
if ((id & 0xFF) != 0xB1) {
return -1;
}
// disable comms watchdog, enable direct drive on all 4 channels
// TODO: should we enable comms watchdog?
write(TLE9104_REG_CFG, 0x0F);
// clear any suprious diag states from startup: first call resets, second reads true state
updateDiagState();
updateDiagState();
// set output enable, clear all other flags
write(TLE9104_REG_GLOBAL_STATUS, 0x80);
// Set hardware enable
m_en.setValue(true);
return 0;
}
int Tle9104::writePad(size_t pin, int value) {
// Inverted since TLE9104 is active low (falling edge to turn on output)
if (value) {
palClearPad(cfg->direct_io[pin].port, cfg->direct_io[pin].pad);
} else {
palSetPad(cfg->direct_io[pin].port, cfg->direct_io[pin].pad);
}
return 0;
}
void Tle9104::updateDiagState() {
spiStart(cfg->spi_bus, &cfg->spi_config);
diag_on12 = read(TLE9104_REG_DIAG_OUT_1_2_ON);
diag_on34 = read(TLE9104_REG_DIAG_OUT_3_4_ON);
diag_off = read(TLE9104_REG_DIAG_OFF);
// clear diag states
write(TLE9104_REG_DIAG_OUT_1_2_ON, 0);
write(TLE9104_REG_DIAG_OUT_3_4_ON, 0);
write(TLE9104_REG_DIAG_OFF, 0);
}
brain_pin_diag_e Tle9104::getDiag(size_t pin) {
int off_diag;
int on_diag;
switch (pin) {
case 0:
on_diag = diag_on12;
off_diag = diag_off;
break;
case 1:
on_diag = diag_on12 >> 3;
off_diag = diag_off >> 2;
break;
case 2:
on_diag = diag_on34;
off_diag = diag_off >> 4;
break;
case 3:
on_diag = diag_on34 >> 3;
off_diag = diag_off >> 6;
break;
default:
return PIN_UNKNOWN;
}
// on diag has 3 bits
on_diag = on_diag & 0x7;
// of diag has 2 bits
off_diag = off_diag & 0x3;
int result = 0;
// Decode on-state faults
switch (on_diag) {
case 2:
result |= PIN_SHORT_TO_BAT;
break;
case 3:
// overtemp and overcurrent
result |= PIN_DRIVER_OVERTEMP;
[[fallthrough]];
case 4:
result |= PIN_OVERLOAD;
break;
case 5:
result |= PIN_DRIVER_OVERTEMP;
break;
}
// Decode off-state faults
switch (off_diag) {
case 2:
result |= PIN_OPEN;
break;
case 3:
result |= PIN_SHORT_TO_GND;
break;
}
return (brain_pin_diag_e)result;
}
static Tle9104 chips[4];
void tle9104_add(Gpio base, int index, const tle9104_config* cfg) {
Tle9104& chip = chips[index];
chip.cfg = cfg;
gpiochip_register(base, "TLE9104", chip, 4);
}
void updatetlediag() {
chips[0].updateDiagState();
}
#endif // BOARD_TLE9104_COUNT > 0