L9779: update driver (#3898)
* L9779: update driver * L9779: missed chip_init * L9779: signature
This commit is contained in:
parent
2714bbc883
commit
883e1ce4ae
|
@ -2,6 +2,29 @@
|
||||||
* l9779.cpp
|
* l9779.cpp
|
||||||
*
|
*
|
||||||
* Created on: Jan 10, 2022
|
* Created on: Jan 10, 2022
|
||||||
|
*
|
||||||
|
* Andrey Gusakov, (c) 2022
|
||||||
|
*
|
||||||
|
* Masks/inputs bits:
|
||||||
|
* 0..3 - IGN1 .. 4 - Ignition pre-drivers
|
||||||
|
* Driven by logical-AND of SPI control bit and dedicated parallel input IGNI1...IGNI4
|
||||||
|
* 4..7 - OUT1 .. 4 - Protected low-side drivers with max current 2.2A
|
||||||
|
* Driven by logical-AND of SPI control bit and dedicated parallel input IN1...IN4
|
||||||
|
* 8 - OUT5 - Protected low-side driver with max current 3A
|
||||||
|
* Driven by logical-AND of SPI control bit and dedicated parallel input IN5
|
||||||
|
* 9..10 - OUT6,7 - Protected low-side drivers with max current 5A (O2 heaters)
|
||||||
|
* Driven by logical-AND of SPI control bit and dedicated parallel input IN6, IN7.
|
||||||
|
* 11 - Unused (no OUT8), IN8-PWM is used for stepper
|
||||||
|
* 12..15 - OUT9..12 - Not exist on L9779WD-SPI, TODO: check L9779WD
|
||||||
|
* 16..17 - OUT13..14 - Protected low side relay drivers with max current 600 mA and Low Battery Volatage function
|
||||||
|
* 18..21 - OUT15..18 - Protected low side relay drivers with max current 600 mA
|
||||||
|
* 22 - Unused (no OUT19)
|
||||||
|
* 23 - OUT20 - Protected low side low current driver with max current 50 mA
|
||||||
|
* 24..27 - OUTA..D - Configurable outputs (OD, PP) with max current 0.6 A (for low and high side FETs)
|
||||||
|
* Can be configured for stepper motor driving.
|
||||||
|
* Stepper is controlled by the logic AND between PWM (IN8) input pin and PWM SPI bit.
|
||||||
|
* 28..31 - OUT25..27 - Unused on L9779WD-SPI, TODO for L9779WD
|
||||||
|
* 32 - MR - Main Relay low side driver with max current 0.6 A, automaticly controlled
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
@ -23,6 +46,15 @@
|
||||||
/*==========================================================================*/
|
/*==========================================================================*/
|
||||||
#define DRIVER_NAME "l9779"
|
#define DRIVER_NAME "l9779"
|
||||||
|
|
||||||
|
#define DIAG_PERIOD_MS (7)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
L9779_DISABLED = 0,
|
||||||
|
L9779_WAIT_INIT,
|
||||||
|
L9779_READY,
|
||||||
|
L9779_FAILED
|
||||||
|
} l9779_drv_state;
|
||||||
|
|
||||||
/* SPI communication helpers */
|
/* SPI communication helpers */
|
||||||
/* Out frame */
|
/* Out frame */
|
||||||
/* D0 - parity */
|
/* D0 - parity */
|
||||||
|
@ -35,6 +67,12 @@
|
||||||
#define MSG_SET_ADDR(a) (((a) & 0x1f) << 10)
|
#define MSG_SET_ADDR(a) (((a) & 0x1f) << 10)
|
||||||
/* D15 - x */
|
/* D15 - x */
|
||||||
|
|
||||||
|
/* ADD user for read commands */
|
||||||
|
#define MSG_READ_ADDR (0x10)
|
||||||
|
|
||||||
|
#define MSG_W(a, d) (static_cast<uint16_t>((MSG_SET_ADDR(a) | MSG_SET_DATA(d))))
|
||||||
|
#define MSG_R(a) (static_cast<uint16_t>((MSG_SET_ADDR(MSG_READ_ADDR) | MSG_SET_SUBADDR(d))))
|
||||||
|
|
||||||
/* In frame */
|
/* In frame */
|
||||||
/* D0 - parity */
|
/* D0 - parity */
|
||||||
#define MSG_GET_PARITY(rx) (((rx) >> 0) & 0x01)
|
#define MSG_GET_PARITY(rx) (((rx) >> 0) & 0x01)
|
||||||
|
@ -50,6 +88,16 @@
|
||||||
/* register address that never can be replyed */
|
/* register address that never can be replyed */
|
||||||
#define REG_INVALID 0xff
|
#define REG_INVALID 0xff
|
||||||
|
|
||||||
|
/* Write only registers */
|
||||||
|
#define CMD_CLOCK_UNLOCK_SW_RST(d) MSG_W(0x0c, (d))
|
||||||
|
#define CMD_START_REACT(d) MSG_W(0x0d, (d))
|
||||||
|
#define CMD_CONTR_REG(n, d) MSG_W(0x08 + (n), (d))
|
||||||
|
|
||||||
|
/* Read only registers */
|
||||||
|
|
||||||
|
/* IGN1..4 + OUT1..7 */
|
||||||
|
#define OUT_DIRECT_DRIVE_MASK 0x7ff
|
||||||
|
|
||||||
/*==========================================================================*/
|
/*==========================================================================*/
|
||||||
/* Driver exported variables. */
|
/* Driver exported variables. */
|
||||||
/*==========================================================================*/
|
/*==========================================================================*/
|
||||||
|
@ -71,20 +119,46 @@ struct L9779 : public GpioChip {
|
||||||
bool spi_parity_odd(uint16_t x);
|
bool spi_parity_odd(uint16_t x);
|
||||||
int spi_validate(uint16_t rx);
|
int spi_validate(uint16_t rx);
|
||||||
int spi_rw(uint16_t tx, uint16_t *rx_ptr);
|
int spi_rw(uint16_t tx, uint16_t *rx_ptr);
|
||||||
|
int spi_rw_array(const uint16_t *tx, uint16_t *rx, int n);
|
||||||
|
|
||||||
|
int update_output();
|
||||||
|
int update_direct_output(size_t pin, int value);
|
||||||
|
int wake_driver();
|
||||||
|
|
||||||
|
int chip_reset();
|
||||||
|
int chip_init_data();
|
||||||
|
int chip_init();
|
||||||
|
|
||||||
brain_pin_diag_e getOutputDiag(size_t pin);
|
brain_pin_diag_e getOutputDiag(size_t pin);
|
||||||
brain_pin_diag_e getInputDiag(size_t pin);
|
brain_pin_diag_e getInputDiag(size_t pin);
|
||||||
|
|
||||||
const l9779_config *cfg;
|
const l9779_config *cfg;
|
||||||
|
|
||||||
|
/* thread stuff */
|
||||||
|
thread_t *thread;
|
||||||
|
THD_WORKING_AREA(thread_wa, 256);
|
||||||
|
semaphore_t wake;
|
||||||
|
|
||||||
|
/* state to be sent to chip */
|
||||||
|
uint32_t o_state;
|
||||||
|
/* output enabled mask */
|
||||||
|
uint32_t o_oe_mask;
|
||||||
|
/* cached output registers state - value last send to chip */
|
||||||
|
uint32_t o_data_cached;
|
||||||
|
|
||||||
|
l9779_drv_state drv_state;
|
||||||
|
|
||||||
/* last accessed register, for validation on next SPI access */
|
/* last accessed register, for validation on next SPI access */
|
||||||
uint8_t last_reg;
|
uint8_t last_reg;
|
||||||
|
|
||||||
|
/* chip needs reintialization due to some critical issue */
|
||||||
|
bool need_init;
|
||||||
|
|
||||||
/* statistic */
|
/* statistic */
|
||||||
//int por_cnt;
|
//int por_cnt;
|
||||||
//int wdr_cnt;
|
//int wdr_cnt;
|
||||||
//int comfe_cnt;
|
//int comfe_cnt;
|
||||||
//int init_cnt;
|
int init_cnt;
|
||||||
//int init_req_cnt;
|
//int init_req_cnt;
|
||||||
int spi_cnt;
|
int spi_cnt;
|
||||||
int spi_err_parity; /* parity errors in rx data */
|
int spi_err_parity; /* parity errors in rx data */
|
||||||
|
@ -104,7 +178,7 @@ static const char* l9779_pin_names[L9779_SIGNALS] = {
|
||||||
"L9779.OUT13", "L9779.OUT14", "L9779.OUT15", "L9779.OUT16",
|
"L9779.OUT13", "L9779.OUT14", "L9779.OUT15", "L9779.OUT16",
|
||||||
"L9779.OUT17", "L9779.OUT18", "L9779.OUT19", "L9779.OUT20",
|
"L9779.OUT17", "L9779.OUT18", "L9779.OUT19", "L9779.OUT20",
|
||||||
"L9779.OUTA", "L9779.OUTB", "L9779.OUTC", "L9779.OUTD",
|
"L9779.OUTA", "L9779.OUTB", "L9779.OUTC", "L9779.OUTD",
|
||||||
"L9779.OUT21", "L9779.OUT22", "L9779.OUT23", "L9779.OUT24",
|
"L9779.OUT25", "L9779.OUT26", "L9779.OUT27", "L9779.OUT28",
|
||||||
"L9779.MRD", "L9779.KEY"
|
"L9779.MRD", "L9779.KEY"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -192,6 +266,265 @@ int L9779::spi_rw(uint16_t tx, uint16_t *rx_ptr)
|
||||||
/* no errors for now */
|
/* no errors for now */
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @return -1 in case of communication error
|
||||||
|
*/
|
||||||
|
int L9779::spi_rw_array(const uint16_t *tx, uint16_t *rx, int n)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint16_t rxdata;
|
||||||
|
SPIDriver *spi = cfg->spi_bus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, &cfg->spi_config);
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
/* Slave Select assertion. */
|
||||||
|
spiSelect(spi);
|
||||||
|
/* data transfer */
|
||||||
|
rxdata = spiPolledExchange(spi, tx[i]);
|
||||||
|
|
||||||
|
if (rx)
|
||||||
|
rx[i] = rxdata;
|
||||||
|
/* Slave Select de-assertion. */
|
||||||
|
spiUnselect(spi);
|
||||||
|
|
||||||
|
/* statistic and debug */
|
||||||
|
this->tx = tx[i];
|
||||||
|
this->rx = rxdata;
|
||||||
|
this->spi_cnt++;
|
||||||
|
|
||||||
|
/* validate reply and save last accessed register */
|
||||||
|
ret = spi_validate(rxdata);
|
||||||
|
last_reg = getRegisterFromResponse(tx[i]);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Ownership release. */
|
||||||
|
spiReleaseBus(spi);
|
||||||
|
|
||||||
|
/* no errors for now */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* use datasheet numbering, starting from 1, skip 4 ignition channels */
|
||||||
|
#define OUT_ENABLED(n) (!!(o_state & BIT((n) + L9779_OUTPUTS_IGN - 1)))
|
||||||
|
#define SHIFT_N_OUT_TO_M(n, m) (OUT_ENABLED(n) << (m))
|
||||||
|
|
||||||
|
/* use datasheet numbering, starting from 1 */
|
||||||
|
#define IGN_ENABLED(n) (!!(o_state & BIT((n) - 1)))
|
||||||
|
#define SHIFT_N_IGN_TO_M(n, m) (IGN_ENABLED(n) << (m))
|
||||||
|
|
||||||
|
int L9779::update_output()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t regs[4];
|
||||||
|
|
||||||
|
/* set value only for non-direct driven pins */
|
||||||
|
uint32_t o_data = o_state & ~OUT_DIRECT_DRIVE_MASK;
|
||||||
|
/* direct driven outputs are logicaly-AND spi bit and dedicated input
|
||||||
|
* set bits to all enabled direct driven outputs */
|
||||||
|
o_data = o_state | (o_oe_mask & OUT_DIRECT_DRIVE_MASK);
|
||||||
|
|
||||||
|
/* nightmare... briliant mapping */
|
||||||
|
regs[0] =
|
||||||
|
SHIFT_N_OUT_TO_M( 1, 7) | /* bit 7 - OUT1 */
|
||||||
|
SHIFT_N_OUT_TO_M( 2, 6) | /* and so on, refer to datasheet */
|
||||||
|
SHIFT_N_OUT_TO_M( 3, 5) |
|
||||||
|
SHIFT_N_OUT_TO_M( 4, 4) |
|
||||||
|
SHIFT_N_OUT_TO_M( 5, 3) |
|
||||||
|
SHIFT_N_OUT_TO_M(20, 2);
|
||||||
|
regs[1] =
|
||||||
|
SHIFT_N_OUT_TO_M(15, 7) |
|
||||||
|
SHIFT_N_OUT_TO_M(14, 6) |
|
||||||
|
/* reserved + don't care */
|
||||||
|
SHIFT_N_IGN_TO_M( 1, 3) |
|
||||||
|
SHIFT_N_IGN_TO_M( 2, 2) |
|
||||||
|
SHIFT_N_IGN_TO_M( 3, 1) |
|
||||||
|
SHIFT_N_IGN_TO_M( 4, 0);
|
||||||
|
regs[2] =
|
||||||
|
SHIFT_N_OUT_TO_M(22, 7) | /* TODO: stepper DIR */
|
||||||
|
SHIFT_N_OUT_TO_M(21, 6) | /* TODO: stepper enable */
|
||||||
|
SHIFT_N_OUT_TO_M(16, 5) |
|
||||||
|
SHIFT_N_OUT_TO_M(14, 4) |
|
||||||
|
SHIFT_N_OUT_TO_M(17, 3) |
|
||||||
|
SHIFT_N_OUT_TO_M(18, 2) |
|
||||||
|
SHIFT_N_OUT_TO_M( 7, 1) |
|
||||||
|
SHIFT_N_OUT_TO_M( 6, 0);
|
||||||
|
regs[3] =
|
||||||
|
SHIFT_N_OUT_TO_M(28, 5) |
|
||||||
|
SHIFT_N_OUT_TO_M(27, 4) |
|
||||||
|
SHIFT_N_OUT_TO_M(26, 3) |
|
||||||
|
SHIFT_N_OUT_TO_M(25, 2) |
|
||||||
|
SHIFT_N_OUT_TO_M(24, 1) |
|
||||||
|
SHIFT_N_OUT_TO_M(23, 0); /* TODO: stepper PWM */
|
||||||
|
uint16_t tx[] = {
|
||||||
|
/* output enables */
|
||||||
|
CMD_CONTR_REG(0, regs[0]),
|
||||||
|
CMD_CONTR_REG(1, regs[1]),
|
||||||
|
CMD_CONTR_REG(2, regs[2]),
|
||||||
|
CMD_CONTR_REG(3, regs[3])
|
||||||
|
};
|
||||||
|
ret = spi_rw_array(tx, NULL, ARRAY_SIZE(tx));
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/* atomic */
|
||||||
|
o_data_cached = o_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int L9779::update_direct_output(size_t pin, int value)
|
||||||
|
{
|
||||||
|
/* no direct-drive gpio is allocated for this output */
|
||||||
|
if (cfg->direct_gpio[pin].port == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
palSetPort(cfg->direct_gpio[pin].port,
|
||||||
|
PAL_PORT_BIT(cfg->direct_gpio[pin].pad));
|
||||||
|
else
|
||||||
|
palClearPort(cfg->direct_gpio[pin].port,
|
||||||
|
PAL_PORT_BIT(cfg->direct_gpio[pin].pad));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief L9779 chip driver wakeup.
|
||||||
|
* @details Wake up driver. Will cause output register update
|
||||||
|
*/
|
||||||
|
|
||||||
|
int L9779::wake_driver()
|
||||||
|
{
|
||||||
|
/* Entering a reentrant critical zone.*/
|
||||||
|
chibios_rt::CriticalSectionLocker csl;
|
||||||
|
chSemSignalI(&wake);
|
||||||
|
if (!port_is_isr_context()) {
|
||||||
|
/**
|
||||||
|
* chSemSignalI above requires rescheduling
|
||||||
|
* interrupt handlers have implicit rescheduling
|
||||||
|
*/
|
||||||
|
chSchRescheduleS();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int L9779::chip_reset() {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = spi_rw(CMD_CLOCK_UNLOCK_SW_RST(BIT(1)), NULL);
|
||||||
|
/**
|
||||||
|
* ???
|
||||||
|
*/
|
||||||
|
chThdSleepMilliseconds(3);
|
||||||
|
|
||||||
|
last_reg = REG_INVALID;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*==========================================================================*/
|
||||||
|
/* Driver thread. */
|
||||||
|
/*==========================================================================*/
|
||||||
|
|
||||||
|
static THD_FUNCTION(l9779_driver_thread, p) {
|
||||||
|
L9779 *chip = reinterpret_cast<L9779*>(p);
|
||||||
|
sysinterval_t poll_interval = 0;
|
||||||
|
|
||||||
|
chRegSetThreadName(DRIVER_NAME);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int ret;
|
||||||
|
msg_t msg = chSemWaitTimeout(&chip->wake, poll_interval);
|
||||||
|
|
||||||
|
/* should we care about msg == MSG_TIMEOUT? */
|
||||||
|
(void)msg;
|
||||||
|
|
||||||
|
/* default polling interval */
|
||||||
|
poll_interval = TIME_MS2I(DIAG_PERIOD_MS);
|
||||||
|
|
||||||
|
if ((chip->cfg == NULL) ||
|
||||||
|
(chip->drv_state == L9779_DISABLED) ||
|
||||||
|
(chip->drv_state == L9779_FAILED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool wd_happy = chip->wd_happy;
|
||||||
|
|
||||||
|
/* update outputs only if WD is happy */
|
||||||
|
if ((wd_happy) || (1)) {
|
||||||
|
ret = chip->update_output();
|
||||||
|
if (ret) {
|
||||||
|
/* set state to L9779_FAILED? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = chip->wd_feed();
|
||||||
|
if (ret < 0) {
|
||||||
|
/* WD is not happy */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* happiness state has changed! */
|
||||||
|
if ((chip->wd_happy != wd_happy) && (chip->wd_happy)) {
|
||||||
|
chip->need_init = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (chip->need_init) {
|
||||||
|
/* clear first, as flag can be raised again during init */
|
||||||
|
chip->need_init = false;
|
||||||
|
/* re-init chip! */
|
||||||
|
chip->chip_init();
|
||||||
|
/* sync pins state */
|
||||||
|
chip->update_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chip is ready to rock? */
|
||||||
|
if (chip->need_init == false) {
|
||||||
|
/* Just update outputs state */
|
||||||
|
ret = chip->update_output();
|
||||||
|
if (ret) {
|
||||||
|
/* set state to L9779_FAILED? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (chip->diag_ts <= chVTGetSystemTimeX()) {
|
||||||
|
/* this is expensive call, will do a lot of spi transfers... */
|
||||||
|
ret = chip->update_status_and_diag();
|
||||||
|
if (ret) {
|
||||||
|
/* set state to L9779_FAILED or force reinit? */
|
||||||
|
} else {
|
||||||
|
diagResponse.reset();
|
||||||
|
}
|
||||||
|
/* TODO:
|
||||||
|
* Procedure to switch on after failure condition occurred:
|
||||||
|
* - Read out of diagnosis bits
|
||||||
|
* - Second read out to verify that the failure conditions are not
|
||||||
|
* remaining
|
||||||
|
* - Set of the dedicated output enable bit of the affected channel
|
||||||
|
* if the diagnosis bit is not active anymore
|
||||||
|
* - Switch on of the channel */
|
||||||
|
|
||||||
|
chip->diag_ts = chTimeAddX(chVTGetSystemTimeX(), TIME_MS2I(DIAG_PERIOD_MS));
|
||||||
|
}
|
||||||
|
poll_interval = chip->calc_sleep_interval();
|
||||||
|
#endif
|
||||||
|
/* default poll_interval */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*==========================================================================*/
|
/*==========================================================================*/
|
||||||
/* Driver interrupt handlers. */
|
/* Driver interrupt handlers. */
|
||||||
|
@ -213,7 +546,22 @@ int L9779::writePad(unsigned int pin, int value) {
|
||||||
if (pin >= L9779_OUTPUTS)
|
if (pin >= L9779_OUTPUTS)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
(void)value;
|
{
|
||||||
|
chibios_rt::CriticalSectionLocker csl;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
o_state |= (1 << pin);
|
||||||
|
} else {
|
||||||
|
o_state &= ~(1 << pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* direct driven? */
|
||||||
|
if (OUT_DIRECT_DRIVE_MASK & BIT(pin)) {
|
||||||
|
return update_direct_output(pin, value);
|
||||||
|
} else {
|
||||||
|
return wake_driver();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -242,7 +590,7 @@ int L9779::readPad(size_t pin) {
|
||||||
|
|
||||||
brain_pin_diag_e L9779::getDiag(size_t pin)
|
brain_pin_diag_e L9779::getDiag(size_t pin)
|
||||||
{
|
{
|
||||||
if (pin >= TLE8888_SIGNALS)
|
if (pin >= L9779_SIGNALS)
|
||||||
return PIN_INVALID;
|
return PIN_INVALID;
|
||||||
|
|
||||||
if (pin < L9779_OUTPUTS)
|
if (pin < L9779_OUTPUTS)
|
||||||
|
@ -251,8 +599,99 @@ brain_pin_diag_e L9779::getDiag(size_t pin)
|
||||||
return getInputDiag(pin);
|
return getInputDiag(pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int L9779::chip_init_data(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
o_oe_mask = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < L9779_DIRECT_OUTPUTS; i++) {
|
||||||
|
if (cfg->direct_gpio[i].port == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* configure source gpio */
|
||||||
|
ret = gpio_pin_markUsed(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad, DRIVER_NAME " DIRECT IO");
|
||||||
|
if (ret) {
|
||||||
|
ret = -1;
|
||||||
|
goto err_gpios;
|
||||||
|
}
|
||||||
|
palSetPadMode(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad, PAL_MODE_OUTPUT_PUSHPULL);
|
||||||
|
palClearPort(cfg->direct_gpio[i].port, PAL_PORT_BIT(cfg->direct_gpio[i].pad));
|
||||||
|
|
||||||
|
/* enable output */
|
||||||
|
o_oe_mask |= BIT(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable all spi-driven ouputs
|
||||||
|
* TODO: add API to enable/disable? */
|
||||||
|
o_oe_mask |= ~OUT_DIRECT_DRIVE_MASK;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_gpios:
|
||||||
|
/* unmark pins */
|
||||||
|
for (int i = 0; i < L9779_DIRECT_OUTPUTS; i++) {
|
||||||
|
if (cfg->direct_gpio[i].port) {
|
||||||
|
gpio_pin_markUnused(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int L9779::chip_init()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* statistic */
|
||||||
|
init_cnt++;
|
||||||
|
|
||||||
|
/* Unlock, while unlocked by default. */
|
||||||
|
ret = spi_rw(CMD_CLOCK_UNLOCK_SW_RST(0), NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Enable power stages */
|
||||||
|
ret = spi_rw(CMD_START_REACT(BIT(1)), NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* TODO: add spi communication test: read IDENT_REG */
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int L9779::init()
|
int L9779::init()
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* check for multiple init */
|
||||||
|
if (drv_state != L9779_WAIT_INIT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = chip_reset();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = chip_init_data();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* force chip init from driver thread */
|
||||||
|
need_init = true;
|
||||||
|
|
||||||
|
/* instance is ready */
|
||||||
|
drv_state = L9779_READY;
|
||||||
|
|
||||||
|
/* init semaphore */
|
||||||
|
chSemObjectInit(&wake, 10);
|
||||||
|
|
||||||
|
/* start thread */
|
||||||
|
thread = chThdCreateStatic(thread_wa, sizeof(thread_wa),
|
||||||
|
PRIO_GPIOCHIP, l9779_driver_thread, this);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,17 +13,29 @@
|
||||||
#include <hal.h>
|
#include <hal.h>
|
||||||
#include "efifeatures.h"
|
#include "efifeatures.h"
|
||||||
|
|
||||||
|
#define L9779_OUTPUTS_IGN (4)
|
||||||
|
/* 4 x IGNI, IN1..IN6, PWM (IN7) */
|
||||||
|
#define L9779_DIRECT_OUTPUTS (L9779_OUTPUTS_IGN + 7)
|
||||||
|
#define L9779_OUTPUTS (L9779_OUTPUTS_IGN + 28 + 1)
|
||||||
|
#define L9779_INPUTS (1)
|
||||||
|
|
||||||
|
#define L9779_SIGNALS (L9779_OUTPUTS + L9779_INPUTS)
|
||||||
|
|
||||||
struct l9779_config {
|
struct l9779_config {
|
||||||
#if HAL_USE_SPI
|
#if HAL_USE_SPI
|
||||||
SPIDriver *spi_bus;
|
SPIDriver *spi_bus;
|
||||||
SPIConfig spi_config;
|
SPIConfig spi_config;
|
||||||
#endif
|
#endif
|
||||||
|
/* MCU port-pin routed to IGN1..IGN4, IN1..7 */
|
||||||
|
struct {
|
||||||
|
ioportid_t port;
|
||||||
|
uint_fast8_t pad;
|
||||||
|
} direct_gpio[L9779_DIRECT_OUTPUTS];
|
||||||
|
/* PWM(IN8) */
|
||||||
|
struct {
|
||||||
|
ioportid_t port;
|
||||||
|
uint_fast8_t pad;
|
||||||
|
} pwm_gpio;
|
||||||
};
|
};
|
||||||
|
|
||||||
#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);
|
int l9779_add(brain_pin_e base, unsigned int index, const l9779_config *cfg);
|
||||||
|
|
|
@ -116,6 +116,23 @@ struct l9779_config l9779_cfg = {
|
||||||
0,
|
0,
|
||||||
.cr2 = SPI_CR2_16BIT_MODE
|
.cr2 = SPI_CR2_16BIT_MODE
|
||||||
},
|
},
|
||||||
|
.direct_gpio = {
|
||||||
|
/* IGNI1..IGNI4 */
|
||||||
|
[0] = {.port = NULL, .pad = 0},
|
||||||
|
[1] = {.port = NULL, .pad = 0},
|
||||||
|
[2] = {.port = NULL, .pad = 0},
|
||||||
|
[3] = {.port = NULL, .pad = 0},
|
||||||
|
/* IN1..IN7 */
|
||||||
|
[4] = {.port = NULL, .pad = 0},
|
||||||
|
[5] = {.port = NULL, .pad = 0},
|
||||||
|
[6] = {.port = NULL, .pad = 0},
|
||||||
|
[7] = {.port = NULL, .pad = 0},
|
||||||
|
[8] = {.port = NULL, .pad = 0},
|
||||||
|
[9] = {.port = NULL, .pad = 0},
|
||||||
|
[10] = {.port = NULL, .pad = 0},
|
||||||
|
},
|
||||||
|
/* PWM (IN8) */
|
||||||
|
.pwm_gpio = {.port = NULL, .pad = 0},
|
||||||
};
|
};
|
||||||
#endif /* (BOARD_L9779_COUNT > 0) */
|
#endif /* (BOARD_L9779_COUNT > 0) */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue