rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
tle8888.cpp
Go to the documentation of this file.
1/*
2 * tle8888.c
3 *
4 * TLE8888 Engine Machine System IC driver
5 *
6 * This has worked on a bench - see https://youtu.be/yjs5dh_NKo4
7 * All SPI and CS pin in OM_DEFAULT mode
8 *
9 * @date Mar 25, 2019
10 * @author Andrey Belomutskiy, (c) 2012-2020
11 *
12 * 3.2 Pin Definitions and Functions
13 *
14 * IN1-4 Parallel input; Input pin for direct control of power stage OUT1-4
15 *
16 * IN5-8 Parallel input; Input pin for direct control of push pull state IGN1-IGN4
17 *
18 * Table 24 Direct Drive Input Assignment to Output Stages
19 * IN9 to IN12 OUT5 to OUT24
20 *
21 * Masks/inputs bits:
22 * 0..3 - OUT1 .. 4 - INJ - OpenDrain: 2.2A - direct
23 * 4..6 - OUT5 .. 7 - OpenDrain: 4.5A
24 * 7..12 - OUT8 ..13 - Push/Pull: 20mA - quite weak
25 * 13..19 - OUT14..20 - OpenDrain: 0.6A
26 * 20..23 - OUT21..24 - Push/Pull: 0.6A - half bridge (GND or +12v)
27 * 24..27 - IGN1 .. 4 - Push/Pull: 20mA - direct
28 *
29 * Andrey Gusakov, (c) 2019
30 */
31
32#include "pch.h"
33
34#include "gpio/tle8888.h"
35
36#if (BOARD_TLE8888_COUNT > 0)
37
39#include "hardware.h"
40#include "gpio/gpio_ext.h"
41
42static Timer diagResponse;
43
44/*
45 * TODO list:
46 */
47
48/*==========================================================================*/
49/* Driver local definitions. */
50/*==========================================================================*/
51
52#define DRIVER_NAME "tle8888"
53
60
61/* SPI communication helpers */
62/* C0 */
63#define CMD_READ (0 << 0)
64#define CMD_WRITE (1 << 0)
65/* C7:1 */
66#define CMD_REG_ADDR(a) (((a) & 0x7f) << 1)
67/* CD7:0 */
68#define CMD_REG_DATA(d) (((d) & 0xff) << 8)
69
70#define CMD_W(a, d) (static_cast<uint16_t>((CMD_WRITE | CMD_REG_ADDR(a) | CMD_REG_DATA(d))))
71#define CMD_R(a) (static_cast<uint16_t>((CMD_READ | CMD_REG_ADDR(a))))
72
73#define REG_INVALID 0x00
74
75/* Command registers */
76#define CMD_CMD0(d) CMD_W(0x01, d)
77#define REG_CMD0_MRSE BIT(0)
78#define REG_CMD0_MRON BIT(1)
79/* Window watchdog open WWDOWT window time = 12.8 mS - fixed value for TLE8888QK */
80#define CMD_WWDSERVICECMD CMD_W(0x15, 0x03)
81#define CMD_FWDRESPCMD(d) CMD_W(0x16, d)
82#define CMD_FWDRESPSYNCCMD(d) CMD_W(0x17, d)
83
84#define CMD_SR_CODE 0x1a
85#define CMD_SR CMD_W(CMD_SR_CODE, 0x03)
86#define CMD_OE_SET CMD_W(0x1c, 0x02)
87#define CMD_OE_CLR CMD_W(0x1c, 0x01)
88#define CMD_CHIP_UNLOCK CMD_W(0x1e, 0x01)
89//#define CMD_CHIP_LOCK CMD_W(0x1e, 0x02)
90
91/* Diagnostic registers */
92#define REG_DIAG(n) (0x20 + ((n) & 0x01))
93#define CMD_DIAG(n) CMD_R(REG_DIAG(n))
94#define CMD_VRSDIAG(n) CMD_R(0x22 + ((n) & 0x01))
95#define CMD_COMDIAG CMD_R(0x24)
96#define CMD_OUTDIAG(n) CMD_R(0x25 + ((n) & 0x07))
97#define CMD_PPOVDIAG CMD_R(0x2a)
98#define CMD_BRIDIAG(n) CMD_R(0x2b + ((n) & 0x01))
99#define CMD_IGNDIAG CMD_R(0x2d)
100#define CMD_WDDIAG CMD_R(0x2e)
101
102/* Status registers */
103#define REG_OPSTAT(n) (0x34 + ((n) & 0x01))
104#define CMD_OPSTAT(n) CMD_R(REG_OPSTAT(n))
105#define REG_OPSTAT_MR BIT(3)
106#define REG_OPSTAT_WAKE BIT(1)
107#define REG_OPSTAT_KEY BIT(0)
108#define REG_WWDSTAT 0x36
109#define CMD_WWDSTAT CMD_R(REG_WWDSTAT)
110#define REG_FWDSTAT(n) (0x37 + ((n) & 0x01))
111#define CMD_FWDSTAT(n) CMD_R(REG_FWDSTAT(n))
112#define REG_TECSTAT 0x39
113#define CMD_TECSTAT CMD_R(REG_TECSTAT)
114
115/* Configuration registers */
116#define CMD_OUTCONFIG(n, d) CMD_W(0x40 + (n), d)
117#define CMD_BRICONFIG(n, d) CMD_W(0x46 + ((n) & 0x01), d)
118#define CMD_IGNCONFIG(d) CMD_W(0x48, d)
119#define CMD_VRSCONFIG(n, d) CMD_W(0x49 + ((n) & 0x03), d)
120#define CMD_OPCONFIG0(d) CMD_W(0x4e, d)
121#define CMD_INCONFIG(n, d) CMD_W(0x53 + ((n) & 0x03), d)
122#define CMD_DDCONFIG(n, d) CMD_W(0x57 + ((n) & 0x03), d)
123#define CMD_OECONFIG(n, d) CMD_W(0x5b + ((n) & 0x03), d)
124
125/* Control registers */
126#define CMD_CONT(n, d) CMD_W(0x7b + ((n) & 0x03), d)
127
128/* Looks like reset value is 113.6ms? 1.6ms * 0x47 */
129#define FWD_PERIOD_MS (20)
130
131/* Default closed window time is 0b0011.1111 * 1.6 = 100.8mS
132 * Default open window time is 0b0011 * 3.2 = 12.8 mS */
133#define WWD_PERIOD_MS (100.8 + (12.8 / 2))
134
135/* TODO: add irq support */
136#define DIAG_PERIOD_MS (7)
137
138const uint8_t tle8888_fwd_responses[16][4] = {
139 /* Reverse order:
140 * RESP3,RESP2,RESP1,RESP0 */
141 {0xFF, 0x0F, 0xF0, 0x00},
142 {0xB0, 0x40, 0xBF, 0x4F},
143 {0xE9, 0x19, 0xE6, 0x16},
144 {0xA6, 0x56, 0xA9, 0x59},
145 {0x75, 0x85, 0x7A, 0x8A},
146 {0x3A, 0xCA, 0x35, 0xC5},
147 {0x63, 0x93, 0x6C, 0x9C},
148 {0x2C, 0xDC, 0x23, 0xD3},
149 {0xD2, 0x22, 0xDD, 0x2D},
150 {0x9D, 0x6D, 0x92, 0x62},
151 {0xC4, 0x34, 0xCB, 0x3B},
152 {0x8B, 0x7B, 0x84, 0x74},
153 {0x58, 0xA8, 0x57, 0xA7},
154 {0x17, 0xE7, 0x18, 0xE8},
155 {0x4E, 0xBE, 0x41, 0xB1},
156 {0x01, 0xF1, 0x0E, 0xFE}
157};
158
159/*==========================================================================*/
160/* Driver exported variables. */
161/*==========================================================================*/
162
163/*==========================================================================*/
164/* Driver local variables and types. */
165/*==========================================================================*/
166
167/* Driver private data */
168struct Tle8888 : public GpioChip {
169 int init() override;
170 int deinit() override;
171
172 int setPadMode(size_t pin, iomode_t mode) override;
173 int writePad(size_t pin, int value) override;
174 int readPad(size_t pin) override;
175 brain_pin_diag_e getDiag(size_t pin) override;
176
177 // internal functions
178 void read_reg(uint16_t reg, uint16_t* val);
179 int spi_rw(uint16_t tx, uint16_t *rx_ptr);
180 int spi_validate(uint16_t rx);
181 int spi_rw_array(const uint16_t *tx, uint16_t *rx, int n);
182 int update_status_and_diag();
183
184 int update_output();
185 int update_direct_output(size_t pin, int value);
186 int wake_driver();
187
188 int chip_reset();
189
190 int chip_init();
191
192 int wwd_feed();
193 int fwd_feed();
194 int wd_get_status();
195 int wd_feed();
196 int calc_sleep_interval();
197
198 brain_pin_diag_e getOutputDiag(size_t pin);
199 brain_pin_diag_e getInputDiag(size_t pin);
200
201 int chip_init_data();
202
203 const tle8888_config *cfg;
204
205 /* thread stuff */
206 thread_t *thread;
207 THD_WORKING_AREA(thread_wa, 256);
208 semaphore_t wake;
209
210 /* state to be sent to chip */
211 uint32_t o_state;
212 /* direct driven output mask */
213 uint32_t o_direct_mask;
214 /* output enabled mask */
215 uint32_t o_oe_mask;
216 /* push-pull enabled mask (for OUT21..OUT24 only) */
217 /* this is overhead to store 4 bits in uint32_t
218 * but I don't want any magic shift math */
219 uint32_t o_pp_mask;
220 /* cached output registers state - value last send to chip */
221 uint32_t o_data_cached;
222
223 tle8888_drv_state drv_state;
224
225 /* direct drive mapping registers */
226 uint8_t InConfig[TLE8888_DIRECT_MISC];
227
228 /* last accessed register, for validation on next SPI access */
229 uint8_t last_reg;
230
231 /* diagnostic registers */
232 uint8_t OutDiag[5];
233 uint8_t PPOVDiag;
234 uint8_t BriDiag[2];
235 uint8_t IgnDiag;
236 /* status registers */
237 uint8_t OpStat[2];
238
239 /* last diagnostic was read */
240 systime_t diag_ts;
241
242 /* WD stuff */
243 uint8_t wwd_err_cnt;
244 uint8_t fwd_err_cnt;
245 uint8_t tot_err_cnt;
246 uint8_t wd_diag;
247 bool wd_happy;
248 systime_t wwd_ts;
249 systime_t fwd_ts;
250
251 /* main relay output */
252 bool mr_manual;
253
254 /* statistic */
255 int por_cnt;
256 int wdr_cnt;
257 int comfe_cnt;
258 int init_req_cnt;
259 int spi_cnt;
260 uint16_t recentTx;
261 uint16_t recentRx;
262};
263
264static Tle8888 chips[BOARD_TLE8888_COUNT];
265
266static const char* tle8888_pin_names[TLE8888_SIGNALS] = {
267 "TLE8888.INJ1", "TLE8888.INJ2", "TLE8888.INJ3", "TLE8888.INJ4",
268 "TLE8888.OUT5", "TLE8888.OUT6", "TLE8888.OUT7", "TLE8888.OUT8",
269 "TLE8888.OUT9", "TLE8888.OUT10", "TLE8888.OUT11", "TLE8888.OUT12",
270 "TLE8888.OUT13", "TLE8888.OUT14", "TLE8888.OUT15", "TLE8888.OUT16",
271 "TLE8888.OUT17", "TLE8888.OUT18", "TLE8888.OUT19", "TLE8888.OUT20",
272 "TLE8888.OUT21", "TLE8888.OUT22", "TLE8888.OUT23", "TLE8888.OUT24",
273 "TLE8888.IGN1", "TLE8888.IGN2", "TLE8888.IGN3", "TLE8888.IGN4",
274 "TLE8888.MR", "TLE8888.KEY", "TLE8888.WAKE"
275};
276
277#if EFI_TUNER_STUDIO
278// set debug_mode 31
280 Tle8888 *chip = &chips[0];
281
282 engine->outputChannels.debugIntField1 = chip->wwd_err_cnt;
283 engine->outputChannels.debugIntField2 = chip->fwd_err_cnt;
284 engine->outputChannels.debugIntField3 = chip->tot_err_cnt;
285 //engine->outputChannels.debugIntField1 = chip->spi_cnt;
286 //engine->outputChannels.debugIntField2 = chip->tx;
287 //engine->outputChannels.debugIntField3 = chip->rx;
288 engine->outputChannels.debugIntField5 = chip->init_cnt;
289
290 engine->outputChannels.debugFloatField3 = chip->OpStat[1];
291 engine->outputChannels.debugFloatField4 = chip->por_cnt * 1000000 + chip->init_req_cnt * 10000;
294}
295#endif /* EFI_TUNER_STUDIO */
296
297/*==========================================================================*/
298/* Driver local functions. */
299/*==========================================================================*/
300
301int Tle8888::spi_validate(uint16_t rx)
302{
303 uint8_t reg = getRegisterFromResponse(rx);
304
305 if ((last_reg != REG_INVALID) && (last_reg != reg)) {
306 /* unexpected SPI answers */
307 if (reg == REG_OPSTAT(0)) {
308 /* after power on reset: the address and the content of the
309 * status register OpStat0 is transmitted with the next SPI
310 * transmission */
311 por_cnt++;
312 } else if (reg == REG_FWDSTAT(1)) {
313 /* after watchdog reset: the address and the content of the
314 * diagnosis register FWDStat1 is transmitted with the first
315 * SPI transmission after the low to high transition of RST */
316 wdr_cnt++;
317 } else if (reg == REG_DIAG(0)) {
318 /* after an invalid communication frame: the address and the
319 * content of the diagnosis register Diag0 is transmitted
320 * with the next SPI transmission and the bit COMFE in
321 * diagnosis register ComDiag is set to "1" */
322 comfe_cnt++;
323 }
324 /* during power on reset: SPI commands are ignored, SDO is always
325 * tristate */
326 /* during watchdog reset: SPI commands are ignored, SDO has the
327 * value of the status flag */
328 need_init = true;
329
330 return -1;
331 }
332
333 return 0;
334}
335
336/**
337 * @returns -1 in case of communication error
338 */
339int Tle8888::spi_rw(uint16_t tx, uint16_t *rx_ptr)
340{
341 int ret;
342 uint16_t rx;
343 SPIDriver *spi = cfg->spi_bus;
344
345 /**
346 * 15.1 SPI Protocol
347 *
348 * after a read or write command: the address and content of the selected register
349 * is transmitted with the next SPI transmission (for not existing addresses or
350 * wrong access mode the data is always 0)
351 */
352
353 /* Acquire ownership of the bus. */
354 spiAcquireBus(spi);
355 /* Setup transfer parameters. */
356 spiStart(spi, &cfg->spi_config);
357 /* Slave Select assertion. */
358 spiSelect(spi);
359 /* Atomic transfer operations. */
360 rx = spiPolledExchange(spi, tx);
361 /* Slave Select de-assertion. */
362 spiUnselect(spi);
363 /* Ownership release. */
364 spiReleaseBus(spi);
365
366 /* statisctic and debug */
367 recentTx = tx;
368 recentRx = rx;
369 this->spi_cnt++;
370
371 if (rx_ptr)
372 *rx_ptr = rx;
373
374 /* validate reply and save last accessed register */
375 ret = spi_validate(rx);
376 last_reg = getRegisterFromResponse(tx);
377
378 /* no errors for now */
379 return ret;
380}
381
382/**
383 * @return -1 in case of communication error
384 */
385int Tle8888::spi_rw_array(const uint16_t *tx, uint16_t *rx, int n)
386{
387 int ret = 0;
388 SPIDriver *spi = cfg->spi_bus;
389
390 if (n <= 0) {
391 return -2;
392 }
393
394 /**
395 * 15.1 SPI Protocol
396 *
397 * after a read or write command: the address and content of the selected register
398 * is transmitted with the next SPI transmission (for not existing addresses or
399 * wrong access mode the data is always 0)
400 */
401
402 /* Acquire ownership of the bus. */
403 spiAcquireBus(spi);
404 /* Setup transfer parameters. */
405 spiStart(spi, &cfg->spi_config);
406
407 for (int i = 0; i < n; i++) {
408 /* Slave Select assertion. */
409 spiSelect(spi);
410 /* data transfer */
411 uint16_t rxdata = spiPolledExchange(spi, tx[i]);
412
413 if (rx)
414 rx[i] = rxdata;
415 /* Slave Select de-assertion. */
416 spiUnselect(spi);
417
418 /* statistic and debug */
419 recentTx = tx[i];
420 recentRx = rxdata;
421 this->spi_cnt++;
422
423 /* validate reply and save last accessed register */
424 ret = spi_validate(rxdata);
425 last_reg = getRegisterFromResponse(tx[i]);
426
427 if (ret < 0)
428 break;
429 }
430 /* Ownership release. */
431 spiReleaseBus(spi);
432
433 /* no errors for now */
434 return ret;
435}
436
437/**
438 * @brief TLE8888 send output registers data.
439 * @details Sends ORed data to register.
440 */
441
442int Tle8888::update_output()
443{
444 int i;
445 int ret;
446
447 uint8_t briconfig0 = 0;
448
449 /* calculate briconfig0 */
450 for (i = 20; i < 24; i++) {
451 if (o_pp_mask & BIT(i)) {
452 if (o_state & BIT(i)) {
453 /* low-side switch mode */
454 } else {
455 /* else enable high-side switch mode */
456 briconfig0 |= BIT((i - 20) * 2);
457 }
458 }
459 }
460 /* TODO: set freewheeling bits in briconfig0? */
461 /* TODO: apply hi-Z mask when support will be added */
462
463 /* set value only for non-direct driven pins */
464 uint32_t o_data = o_state & ~o_direct_mask;
465
466 /* output for push-pull pins is allways enabled
467 * (at least until we start supporting hi-Z state) */
468 o_data |= o_pp_mask;
469
470 uint16_t updateTx[] = {
471 /* bridge config */
472 CMD_BRICONFIG(0, briconfig0),
473 /* output enables */
474 CMD_CONT(0, o_data >> 0),
475 CMD_CONT(1, o_data >> 8),
476 CMD_CONT(2, o_data >> 16),
477 CMD_CONT(3, o_data >> 24),
478 /* Main Relay output: manual vs auto-mode */
479 CMD_CMD0((mr_manual ? REG_CMD0_MRSE : 0x0) |
480 ((o_data & BIT(TLE8888_OUTPUT_MR)) ? REG_CMD0_MRON : 0x0))
481 };
482 ret = spi_rw_array(updateTx, NULL, efi::size(updateTx));
483
484 if (ret == 0) {
485 /* atomic */
486 o_data_cached = o_data;
487 }
488
489 return ret;
490}
491
492/**
493 * @brief read TLE8888 diagnostic registers data.
494 * @details Chained read of several registers
495 */
496int Tle8888::update_status_and_diag()
497{
498 int ret = 0;
499 const uint16_t diagTx[] = {
500 CMD_OUTDIAG(0),
501 CMD_OUTDIAG(1),
502 CMD_OUTDIAG(2),
503 CMD_OUTDIAG(3),
504 CMD_OUTDIAG(4),
505 CMD_PPOVDIAG,
506 CMD_BRIDIAG(0),
507 CMD_BRIDIAG(1),
508 CMD_IGNDIAG,
509 CMD_OPSTAT(0),
510 CMD_OPSTAT(1),
511 CMD_OPSTAT(1)
512 };
513 uint16_t rx[efi::size(diagTx)];
514
515 ret = spi_rw_array(diagTx, rx, efi::size(diagTx));
516
517 if (ret == 0) {
518 /* the address and content of the selected register is transmitted with the
519 * next SPI transmission */
520 OutDiag[0] = getDataFromResponse(rx[0 + 1]);
521 OutDiag[1] = getDataFromResponse(rx[1 + 1]);
522 OutDiag[2] = getDataFromResponse(rx[2 + 1]);
523 OutDiag[3] = getDataFromResponse(rx[3 + 1]);
524 OutDiag[4] = getDataFromResponse(rx[4 + 1]);
525 PPOVDiag = getDataFromResponse(rx[5 + 1]);
526 BriDiag[0] = getDataFromResponse(rx[6 + 1]);
527 BriDiag[1] = getDataFromResponse(rx[7 + 1]);
528 IgnDiag = getDataFromResponse(rx[8 + 1]);
529 OpStat[0] = getDataFromResponse(rx[9 + 1]);
530 OpStat[1] = getDataFromResponse(rx[10 + 1]);
531 }
532
533 return ret;
534}
535
536/**
537 * @brief Drives natve MCU pins connected to TLE8888 inputs
538 * @details This is faster than updating Cont registers over SPI
539 */
540
541int Tle8888::update_direct_output(size_t pin, int value)
542{
543 int index = -1;
544
545 if (pin < 4) {
546 /* OUT1..4 */
547 index = pin;
548 } else if (pin >= 24) {
549 /* IGN1..4 */
550 index = (pin - 24) + 4;
551 } else {
552 /* find remapable direct drive gpio */
553 for (int i = 0; i < TLE8888_DIRECT_MISC; i++) {
554 /* again: outputs in cfg counted starting from 1 - hate this */
555 if (cfg->direct_maps[i].output == pin + 1) {
556 index = 8 + i;
557 break;
558 }
559 }
560 }
561
562 /* direct gpio not found */
563 if (index < 0)
564 return -1;
565
566 if (value)
567 palSetPort(cfg->direct_gpio[index].port,
568 PAL_PORT_BIT(cfg->direct_gpio[index].pad));
569 else
570 palClearPort(cfg->direct_gpio[index].port,
571 PAL_PORT_BIT(cfg->direct_gpio[index].pad));
572 return 0;
573}
574
575/**
576 * @brief TLE8888 chip driver wakeup.
577 * @details Wake up driver. Will cause output register update
578 */
579
580int Tle8888::wake_driver()
581{
582 /* Entering a reentrant critical zone.*/
583 chibios_rt::CriticalSectionLocker csl;
584 chSemSignalI(&wake);
585 if (!port_is_isr_context()) {
586 /**
587 * chSemSignalI above requires rescheduling
588 * interrupt handlers have implicit rescheduling
589 */
590 chSchRescheduleS();
591 }
592
593 return 0;
594}
595
597{
598 if (bits == 0x01)
599 return PIN_SHORT_TO_BAT;
600 if (bits == 0x02)
601 return PIN_OPEN;
602 if (bits == 0x03)
603 return PIN_SHORT_TO_GND;
604 return PIN_OK;
605}
606
608{
609 int diag = tle8888_2b_to_diag_no_temp(bits);
610
611 if (diag == PIN_SHORT_TO_BAT)
612 diag |= PIN_DRIVER_OVERTEMP;
613
614 return static_cast<brain_pin_diag_e>(diag);
615}
616
617int Tle8888::chip_reset() {
618 int ret = spi_rw(CMD_SR, NULL);
619 /**
620 * Table 8. Reset Times. All reset times not more than 20uS
621 */
622 chThdSleepMilliseconds(3);
623
624 last_reg = REG_INVALID;
625
626 return ret;
627}
628
629int Tle8888::chip_init()
630{
631 int ret;
632
633 /* statistic */
634 init_cnt++;
635
636 uint16_t initTx[] = {
637 /* unlock */
638 CMD_CHIP_UNLOCK,
639 /* set INCONFIG - aux input mapping */
640 CMD_INCONFIG(0, InConfig[0]),
641 CMD_INCONFIG(1, InConfig[1]),
642 CMD_INCONFIG(2, InConfig[2]),
643 CMD_INCONFIG(3, InConfig[3]),
644 /* Diagnnostic settings */
645 /* Enable open load detection and disable switch off
646 * in case of overcurrent for OUTPUT1..4 */
647 CMD_OUTCONFIG(0, BIT(7) | BIT(5) | BIT(3) | BIT(1)),
648 /* Enable open load detection and disable switch off
649 * in case of overcurrent for OUTPUT5..7 */
650 CMD_OUTCONFIG(1, BIT(5) | BIT(3) | BIT(1)),
651#if 1
652 /* MRE 0.5.? share same outputs between OUTPUT8..13
653 * and analog inputs. Disable diagnostic pull-down
654 * not to affect analog inputs.
655 * Disable open load detection and set short to bat
656 * thresholt to 125 mV (default) for OUTPUT8..13 */
657 CMD_OUTCONFIG(2, (0x0 << 6) | 0x00),
658#else
659 /* Enable open load detection and set short to bat
660 * thresholt to 125 mV (default) for OUTPUT8..13 */
661 CMD_OUTCONFIG(2, (0x0 << 6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0)),
662#endif
663 /* Enable open load detection and disable switch off
664 * in case of overcurrent for OUTPUT14
665 * Set short to bat threshold to 125mV (default) for
666 * OUTPUT12..13 and OUTPUT10..11 */
667 CMD_OUTCONFIG(3, BIT(5) | (0x0 << 2) | (0x0 << 0)),
668 /* No delayed off function for OUTPUT17
669 * Enable open load detection and disable switch off
670 * in case of overcurrent for OUTPUT15..17 */
671 CMD_OUTCONFIG(4, BIT(5) | BIT(3) | BIT(1)),
672 /* Enable open load detection and disable switch off
673 * in case of overcurrent for OUTPUT18..20 */
674 CMD_OUTCONFIG(5, BIT(5) | BIT(3) | BIT(1)),
675 /* set OE and DD registers */
676 CMD_OECONFIG(0, o_oe_mask >> 0),
677 CMD_DDCONFIG(0, o_direct_mask >> 0),
678 CMD_OECONFIG(1, o_oe_mask >> 8),
679 CMD_DDCONFIG(1, o_direct_mask >> 8),
680 CMD_OECONFIG(2, o_oe_mask >> 16),
681 CMD_DDCONFIG(2, o_direct_mask >> 16),
682 CMD_OECONFIG(3, o_oe_mask >> 24),
683 CMD_DDCONFIG(3, o_direct_mask >> 24),
684 /* set VR mode: VRS/Hall */
685 CMD_VRSCONFIG(1, (0 << 4) |
686 (cfg->mode << 2) |
687 (0 << 0)),
688 /* enable outputs */
689 CMD_OE_SET
690 };
691
692 ret = spi_rw_array(initTx, NULL, efi::size(initTx));
693
694 if (ret == 0) {
695 /* enable pins */
696 if (cfg->ign_en.port)
697 palSetPort(cfg->ign_en.port, PAL_PORT_BIT(cfg->ign_en.pad));
698 if (cfg->inj_en.port)
699 palSetPort(cfg->inj_en.port, PAL_PORT_BIT(cfg->inj_en.pad));
700 }
701
704 }
705
706 return ret;
707}
708
709int Tle8888::wwd_feed() {
710 spi_rw(CMD_WWDSERVICECMD, NULL);
711
712 return 0;
713}
714
715int Tle8888::fwd_feed() {
716 uint16_t reg;
717
718 spi_rw(CMD_FWDSTAT(1), NULL);
719 /* here we get response of the 'FWDStat1' above */
720 spi_rw(CMD_WDDIAG, &reg);
721
722 uint8_t data = getDataFromResponse(reg);
723 uint8_t fwdquest = data & 0xF;
724 uint8_t fwdrespc = (data >> 4) & 3;
725 /* Table lines are filled in reverse order (like in DS) */
726 uint8_t response = tle8888_fwd_responses[fwdquest][3 - fwdrespc];
727 if (fwdrespc != 0) {
728 spi_rw(CMD_FWDRESPCMD(response), NULL);
729 } else {
730 /* to restart heartbeat timer, sync command should be used for response 0 */
731 spi_rw(CMD_FWDRESPSYNCCMD(response), NULL);
732 }
733
734 return 0;
735}
736
737int Tle8888::wd_get_status() {
738 uint16_t reg;
739
740 spi_rw(CMD_WDDIAG, NULL);
741 spi_rw(CMD_WDDIAG, &reg);
742
743 wd_diag = getDataFromResponse(reg);
744
745 if (wd_diag & 0x70) {
746 /* Reset caused by TEC
747 * Reset caused by FWD
748 * Reset caused by WWD */
749 return -1;
750 }
751 if (wd_diag & 0x0f) {
752 /* Some error in WD handling */
753 return 1;
754 }
755
756 return 0;
757}
758
759int Tle8888::wd_feed() {
760 bool update_status = false;
761
762 if (wwd_ts <= chVTGetSystemTimeX()) {
763 update_status = true;
764 if (wwd_feed() == 0) {
765 wwd_ts = chTimeAddX(chVTGetSystemTimeX(), TIME_MS2I(WWD_PERIOD_MS));
766 }
767 }
768
769 if (fwd_ts <= chVTGetSystemTimeX()) {
770 update_status = true;
771 if (fwd_feed() == 0) {
772 fwd_ts = chTimeAddX(chVTGetSystemTimeX(), TIME_MS2I(FWD_PERIOD_MS));
773 }
774 }
775
776 if (update_status) {
777 uint16_t wwd_reg, fwd_reg, tec_reg;
778
779 spi_rw(CMD_WWDSTAT, NULL);
780 spi_rw(CMD_FWDSTAT(0), &wwd_reg);
781 spi_rw(CMD_TECSTAT, &fwd_reg);
782 spi_rw(CMD_TECSTAT, &tec_reg);
783
784 wwd_err_cnt = getDataFromResponse(wwd_reg) & 0x7f;
785 fwd_err_cnt = getDataFromResponse(fwd_reg) & 0x7f;
786 tot_err_cnt = getDataFromResponse(tec_reg) & 0x7f;
787
788 wd_happy = ((wwd_err_cnt == 0) &&
789 (fwd_err_cnt == 0));
790
791 return wd_get_status();
792 } else {
793 return 0;
794 }
795}
796
797int Tle8888::calc_sleep_interval() {
798 systime_t now = chVTGetSystemTimeX();
799
800 sysinterval_t wwd_delay = chTimeDiffX(now, wwd_ts);
801 sysinterval_t fwd_delay = chTimeDiffX(now, fwd_ts);
802 sysinterval_t diag_delay = chTimeDiffX(now, diag_ts);
803
804 if ((diag_delay <= wwd_delay) && (diag_delay <= fwd_delay))
805 return diag_delay;
806 if (fwd_delay <= wwd_delay)
807 return fwd_delay;
808 return wwd_delay;
809}
810
811/*==========================================================================*/
812/* Driver thread. */
813/*==========================================================================*/
814
815static THD_FUNCTION(tle8888_driver_thread, p) {
816 Tle8888 *chip = reinterpret_cast<Tle8888*>(p);
817 sysinterval_t poll_interval = 0;
818
819 chRegSetThreadName(DRIVER_NAME);
820
821 while (1) {
822 int ret;
823 msg_t msg = chSemWaitTimeout(&chip->wake, poll_interval);
824
825 /* should we care about msg == MSG_TIMEOUT? */
826 (void)msg;
827
828 /* default polling interval */
829 poll_interval = TIME_MS2I(DIAG_PERIOD_MS);
830
831 if ((chip->cfg == NULL) ||
832 (chip->drv_state == TLE8888_DISABLED) ||
833 (chip->drv_state == TLE8888_FAILED))
834 continue;
835
836 bool wd_happy = chip->wd_happy;
837
838 /* update outputs only if WD is happy */
839 if ((wd_happy) || (1)) {
840 ret = chip->update_output();
841 if (ret) {
842 /* set state to TLE8888_FAILED? */
843 }
844 }
845
846 ret = chip->wd_feed();
847 if (ret < 0) {
848 /* WD is not happy */
849 continue;
850 }
851 /* happiness state has changed! */
852 if ((chip->wd_happy != wd_happy) && (chip->wd_happy)) {
853 chip->need_init = true;
854 }
855
856 if (chip->need_init) {
857 /* clear first, as flag can be raised again during init */
858 chip->need_init = false;
859 /* re-init chip! */
860 chip->chip_init();
861 /* sync pins state */
862 chip->update_output();
863 }
864
865 if (chip->diag_ts <= chVTGetSystemTimeX()) {
866 /* this is expensive call, will do a lot of spi transfers... */
867 ret = chip->update_status_and_diag();
868 if (ret) {
869 /* set state to TLE8888_FAILED or force reinit? */
870 } else {
871 diagResponse.reset();
872 }
873 /* TODO:
874 * Procedure to switch on after failure condition occurred:
875 * - Read out of diagnosis bits
876 * - Second read out to verify that the failure conditions are not
877 * remaining
878 * - Set of the dedicated output enable bit of the affected channel
879 * if the diagnosis bit is not active anymore
880 * - Switch on of the channel */
881
882 chip->diag_ts = chTimeAddX(chVTGetSystemTimeX(), TIME_MS2I(DIAG_PERIOD_MS));
883 }
884
885 poll_interval = chip->calc_sleep_interval();
886 }
887}
888
889/*==========================================================================*/
890/* Driver interrupt handlers. */
891/*==========================================================================*/
892
893/*==========================================================================*/
894/* Driver exported functions. */
895/*==========================================================================*/
896
897int Tle8888::setPadMode(unsigned int pin, iomode_t mode) {
898 if (pin >= TLE8888_SIGNALS)
899 return -1;
900
901 /* if someone has requested MR pin - switch it to manual mode */
902 if (pin == TLE8888_OUTPUT_MR) {
903 mr_manual = true;
904 }
905
906 /* do not enalbe PP mode yet */
907#if 0
908 /* only OUT21..OUT24 support mode change: PP vs OD */
909 if ((pin < 20) || (pin > 23))
910 return 0;
911
912 /* this is absolutly confusing... we pass STM32 specific
913 * values to tle8888 driver... But this is how gpios
914 * currently implemented */
915 if ((mode & PAL_STM32_OTYPE_MASK) == PAL_STM32_OTYPE_OPENDRAIN) {
916 o_pp_mask &= ~BIT(pin);
917 } else {
918 o_pp_mask |= BIT(pin);
919 }
920#else
921 (void)mode;
922#endif
923
924 return 0;
925}
926
927int Tle8888::writePad(unsigned int pin, int value) {
928
929 if (pin >= TLE8888_OUTPUTS)
930 return -1;
931
932 {
933 chibios_rt::CriticalSectionLocker csl;
934
935 if (value) {
936 o_state |= BIT(pin);
937 } else {
938 o_state &= ~BIT(pin);
939 }
940 }
941
942 /* direct driven? */
943 if (o_direct_mask & BIT(pin)) {
944 return update_direct_output(pin, value);
945 } else {
946 return wake_driver();
947 }
948 return 0;
949}
950
951int Tle8888::readPad(size_t pin) {
952 if (pin >= TLE8888_OUTPUTS)
953 return -1;
954
955 if (pin < TLE8888_OUTPUTS_REGULAR) {
956 /* return output state */
957 /* TODO: check that pins is disabled by diagnostic? */
958 return !!(o_data_cached & BIT(pin));
959 } else if (pin == TLE8888_OUTPUT_MR) {
960 /* Main relay can be enabled by KEY input, so report real state */
961 return !!(OpStat[0] & REG_OPSTAT_MR);
962 } else if (pin == TLE8888_INPUT_KEY) {
963 return !!(OpStat[0] & REG_OPSTAT_KEY);
964 } if (pin == TLE8888_INPUT_WAKE) {
965 return !!(OpStat[0] & REG_OPSTAT_WAKE);
966 }
967
968 /* unknown pin */
969 return -1;
970}
971
972brain_pin_diag_e Tle8888::getOutputDiag(size_t pin) {
973 if (diagResponse.hasElapsedMs(500)) {
974 // has been too long since we've received diagnostics
975 return PIN_DRIVER_OFF;
976 }
977 /* OUT1..OUT4, indexes 0..3 */
978 if (pin < 4)
979 return tle8888_2b_to_diag_with_temp((OutDiag[0] >> ((pin - 0) * 2)) & 0x03);
980 /* OUT5..OUT7, indexes 4..6 */
981 if (pin < 7) {
982 return tle8888_2b_to_diag_with_temp((OutDiag[1] >> ((pin - 4) * 2)) & 0x03);
983 }
984 /* OUT8 to OUT13, indexes 7..12 */
985 if (pin < 13) {
986 int ret;
987
988 /* OUT8 */
989 if (pin == 7)
990 ret = tle8888_2b_to_diag_no_temp((OutDiag[1] >> 6) & 0x03);
991 /* OUT9..OUT12 */
992 else if (pin < 12)
993 ret = tle8888_2b_to_diag_no_temp((OutDiag[2] >> ((pin - 8) * 2)) & 0x03);
994 /* OUT13 */
995 else /* if (pin == 12) */
996 ret = tle8888_2b_to_diag_no_temp((OutDiag[3] >> 0) & 0x03);
997
998 /* overvoltage bit */
999 if (PPOVDiag & BIT(pin - 7))
1000 ret |= PIN_SHORT_TO_BAT;
1001
1002 return static_cast<brain_pin_diag_e>(ret);
1003 }
1004 /* OUT14 to OUT16, indexes 13..15 */
1005 if (pin < 16)
1006 return tle8888_2b_to_diag_with_temp((OutDiag[3] >> ((pin - 13 + 1) * 2)) & 0x03);
1007 /* OUT17 to OUT20, indexes 16..19 */
1008 if (pin < 20)
1009 return tle8888_2b_to_diag_with_temp((OutDiag[4] >> ((pin - 16) * 2)) & 0x03);
1010 /* OUT21..OUT24, indexes 20..23 */
1011 if (pin < 24) {
1012 /* half bridges */
1013 int diag;
1014
1015 diag = tle8888_2b_to_diag_no_temp((BriDiag[0] >> ((pin - 20) * 2)) & 0x03);
1016 if (((pin == 22) || (pin == 23)) &&
1017 (BriDiag[1] & BIT(5)))
1018 diag |= PIN_DRIVER_OVERTEMP;
1019 if (((pin == 20) || (pin == 21)) &&
1020 (BriDiag[1] & BIT(4)))
1021 diag |= PIN_DRIVER_OVERTEMP;
1022 if (BriDiag[1] & BIT(pin - 20))
1023 diag |= PIN_OVERLOAD; /* overcurrent */
1024
1025 return static_cast<brain_pin_diag_e>(diag);
1026 }
1027 if (pin < 28)
1028 return tle8888_2b_to_diag_with_temp((IgnDiag >> ((pin - 24) * 2)) & 0x03);
1029
1030 return PIN_OK;
1031}
1032
1033brain_pin_diag_e Tle8888::getInputDiag(unsigned int pin)
1034{
1035 (void)pin;
1036
1037 return PIN_OK;
1038}
1039
1040brain_pin_diag_e Tle8888::getDiag(size_t pin)
1041{
1042 if (pin >= TLE8888_SIGNALS)
1043 return PIN_UNKNOWN;
1044
1045 if (pin < TLE8888_OUTPUTS)
1046 return getOutputDiag(pin);
1047 else
1048 return getInputDiag(pin);
1049}
1050
1051int Tle8888::chip_init_data() {
1052 int ret = 0;
1053
1054 o_direct_mask = 0;
1055 o_oe_mask = 0;
1056 o_pp_mask = 0;
1057
1058 /* mark pins used */
1059 if (cfg->reset.port != NULL) {
1060 ret |= gpio_pin_markUsed(cfg->reset.port, cfg->reset.pad, DRIVER_NAME " RST");
1061 palSetPadMode(cfg->reset.port, cfg->reset.pad, PAL_MODE_OUTPUT_PUSHPULL);
1062 palSetPort(cfg->reset.port, PAL_PORT_BIT(cfg->reset.pad));
1063 }
1064 if (cfg->ign_en.port != NULL) {
1065 ret |= gpio_pin_markUsed(cfg->ign_en.port, cfg->ign_en.pad, DRIVER_NAME " IGN EN");
1066 palSetPadMode(cfg->ign_en.port, cfg->ign_en.pad, PAL_MODE_OUTPUT_PUSHPULL);
1067 palClearPort(cfg->ign_en.port, PAL_PORT_BIT(cfg->ign_en.pad));
1068 }
1069 if (cfg->inj_en.port != NULL) {
1070 ret |= gpio_pin_markUsed(cfg->inj_en.port, cfg->inj_en.pad, DRIVER_NAME " INJ EN");
1071 palSetPadMode(cfg->inj_en.port, cfg->inj_en.pad, PAL_MODE_OUTPUT_PUSHPULL);
1072 palClearPort(cfg->inj_en.port, PAL_PORT_BIT(cfg->inj_en.pad));
1073 }
1074
1075 for (int i = 0; i < TLE8888_DIRECT_MISC; i++) {
1076 /* Set some invalid default OUT number...
1077 * Keeping this register default (0) will map one of input signals
1078 * to OUT5 and no control over SPI for this pin will be possible.
1079 * If some other pin is also mapped to OUT5 both inputs should be
1080 * high (logical AND) to enable OUT5.
1081 * Set non-exist output in case no override is provided in config.
1082 * See code below */
1083 InConfig[i] = 25 - 1 - 4;
1084 }
1085
1086 for (int i = 0; i < TLE8888_DIRECT_OUTPUTS; i++) {
1087 int out = -1;
1088 uint32_t mask;
1089
1090 if (i < 4) {
1091 /* injector */
1092 out = i;
1093 } else if (i < 8) {
1094 /* ignition */
1095 out = (i - 4) + 24;
1096 } else {
1097 /* remappable */
1098 /* in config counted from 1 */
1099 out = cfg->direct_maps[i - 8].output - 1;
1100 }
1101
1102 if ((out < 0) || (cfg->direct_gpio[i].port == NULL)) {
1103 /* now this is safe, InConfig[] is inited with some non-exist output */
1104 continue;
1105 }
1106
1107 /* TODO: implement PP pin driving throught direct gpio */
1108 if ((cfg->stepper) && (out >= 20) && (out <= 23)) {
1109 /* now this is safe, InConfig[] is inited with some non-exist output */
1110 continue;
1111 }
1112
1113 /* calculate mask */
1114 mask = BIT(out);
1115
1116 /* check if output already occupied */
1117 if (o_direct_mask & mask) {
1118 /* incorrect config? */
1119 ret = -1;
1120 goto err_gpios;
1121 }
1122
1123 /* configure source gpio */
1124 ret = gpio_pin_markUsed(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad, DRIVER_NAME " DIRECT IO");
1125 if (ret) {
1126 ret = -1;
1127 goto err_gpios;
1128 }
1129 palSetPadMode(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad, PAL_MODE_OUTPUT_PUSHPULL);
1130 palClearPort(cfg->direct_gpio[i].port, PAL_PORT_BIT(cfg->direct_gpio[i].pad));
1131
1132 /* enable direct drive */
1133 o_direct_mask |= mask;
1134
1135 /* calculate INCONFIG - aux input mapping for IN9..IN12 */
1136 if (i >= 8) {
1137 if ((out < 4) || (out >= 24)) {
1138 ret = -1;
1139 goto err_gpios;
1140 }
1141 InConfig[i - 8] = out - 4;
1142 }
1143 }
1144
1145 /* Enable Push-Pull mode for OUT21..OUT24 */
1146 if (cfg->stepper) {
1147 o_pp_mask |= BIT(20) | BIT(21) | BIT(22) | BIT(23);
1148 }
1149
1150 /* enable all direct driven */
1151 o_oe_mask |= o_direct_mask;
1152
1153 /* enable all ouputs
1154 * TODO: add API to enable/disable? */
1155 o_oe_mask |= 0x0ffffff0;
1156
1157 return 0;
1158
1159err_gpios:
1160 /* unmark pins */
1161 if (cfg->inj_en.port != NULL)
1162 gpio_pin_markUnused(cfg->inj_en.port, cfg->inj_en.pad);
1163 if (cfg->ign_en.port != NULL)
1164 gpio_pin_markUnused(cfg->ign_en.port, cfg->ign_en.pad);
1165 if (cfg->reset.port != NULL)
1166 gpio_pin_markUnused(cfg->reset.port, cfg->reset.pad);
1167 for (int i = 0; i < TLE8888_DIRECT_OUTPUTS; i++) {
1168 if (cfg->direct_gpio[i].port) {
1169 gpio_pin_markUnused(cfg->direct_gpio[i].port, cfg->direct_gpio[i].pad);
1170 }
1171 }
1172
1173 return ret;
1174}
1175
1176int Tle8888::init()
1177{
1178 int ret;
1179
1180 /* check for multiple init */
1181 if (drv_state != TLE8888_WAIT_INIT)
1182 return -1;
1183
1184 ret = chip_reset();
1185 if (ret)
1186 return ret;
1187
1188 ret = chip_init_data();
1189 if (ret)
1190 return ret;
1191
1192 /* force init from driver thread */
1193 need_init = true;
1194
1195 /* instance is ready */
1196 drv_state = TLE8888_READY;
1197
1198 /* init semaphore */
1199 chSemObjectInit(&wake, 10);
1200
1201 /* start thread */
1202 thread = chThdCreateStatic(thread_wa, sizeof(thread_wa),
1203 PRIO_GPIOCHIP, tle8888_driver_thread, this);
1204
1205 return 0;
1206}
1207
1208int Tle8888::deinit()
1209{
1210 /* disable pins */
1211 if (cfg->ign_en.port)
1212 palClearPort(cfg->ign_en.port, PAL_PORT_BIT(cfg->ign_en.pad));
1213 if (cfg->inj_en.port)
1214 palClearPort(cfg->inj_en.port, PAL_PORT_BIT(cfg->inj_en.pad));
1215
1216 /* stop thread */
1217 chThdTerminate(thread);
1218
1219 return 0;
1220}
1221
1222/**
1223 * @brief TLE8888 driver add.
1224 * @details Checks for valid config
1225 * @return return gpio chip base
1226 */
1227
1228int tle8888_add(brain_pin_e base, unsigned int index, const tle8888_config *cfg) {
1229
1230 efiAssert(ObdCode::OBD_PCM_Processor_Fault, cfg != NULL, "8888CFG", 0)
1231
1232 /* no config or no such chip */
1233 if ((!cfg) || (!cfg->spi_bus) || (index >= BOARD_TLE8888_COUNT))
1234 return -1;
1235
1236 /* check for valid chip select.
1237 * TODO: remove this check? CS can be driven by SPI */
1238 if (cfg->spi_config.ssport == NULL)
1239 return -1;
1240
1241 Tle8888* chip = &chips[index];
1242
1243 /* already initted? */
1244 if (chip->cfg)
1245 return -1;
1246
1247 chip->cfg = cfg;
1248 chip->o_state = 0;
1249 chip->o_direct_mask = 0;
1250 chip->o_data_cached = 0;
1251 chip->drv_state = TLE8888_WAIT_INIT;
1252
1253 /* register */
1254 int ret = gpiochip_register(base, DRIVER_NAME, *chip, TLE8888_OUTPUTS);
1255 if (ret < 0)
1256 return ret;
1257
1258 /* set default pin names, board init code can rewrite */
1260
1261 return ret;
1262}
1263
1264/*==========================================================================*/
1265/* Driver exported debug functions. */
1266/*==========================================================================*/
1267void Tle8888::read_reg(uint16_t reg, uint16_t *val)
1268{
1269 spi_rw(CMD_R(reg), val);
1270}
1271
1273 auto& tle = chips[0];
1274
1275 tle.need_init = true;
1276 tle.init_req_cnt++;
1277}
1278
1280 auto& chip = chips[0];
1281
1282 // since responses are always in the NEXT transmission we will have this one first
1283 chip.read_reg(0, NULL);
1284
1285 efiPrintf("register: data");
1286 for (int request = 0; request <= 0x7e + 1; request++) {
1287 uint16_t tmp;
1288 chip.read_reg(request < (0x7e + 1) ? request : 0x7e, &tmp);
1289 uint8_t reg = getRegisterFromResponse(tmp);
1290 uint8_t data = getDataFromResponse(tmp);
1291
1292 efiPrintf("%02x: %02x", reg, data);
1293 }
1294}
1295
1296#else /* BOARD_TLE8888_COUNT > 0 */
1297
1298int tle8888_add(brain_pin_e base, unsigned int index, const tle8888_config *cfg)
1299{
1300 (void)base; (void)index; (void)cfg;
1301
1302 return -1;
1303}
1304
1305#endif /* (BOARD_TLE8888_COUNT > 0) */
TunerStudioOutputChannels outputChannels
Definition engine.h:109
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
Definition core.cpp:186
int gpiochips_setPinNames(brain_pin_e base, const char **names)
Set pins names for registered gpiochip.
Definition core.cpp:266
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
uint32_t iomode_t
Digital I/O modes.
Definition hal_pal_lld.h:83
@ OBD_PCM_Processor_Fault
void gpio_pin_markUnused(ioportid_t port, ioportmask_t pin)
bool gpio_pin_markUsed(ioportid_t port, ioportmask_t pin, const char *msg)
brain_pin_diag_e
brain_pin_e pin
Definition stm32_adc.cpp:15
static THD_WORKING_AREA(storageManagerThreadStack, 3 *UTILITY_THREAD_STACK_SIZE)
virtual brain_pin_diag_e getDiag(size_t)
Definition gpio_ext.h:31
virtual int writePad(size_t, int)
Definition gpio_ext.h:28
virtual int readPad(size_t)
Definition gpio_ext.h:29
virtual int deinit()
Definition gpio_ext.h:32
virtual int init()=0
virtual int setPadMode(size_t, iomode_t)
Definition gpio_ext.h:27
SPIConfig spi_config
Definition tle8888.h:44
SPIDriver * spi_bus
Definition tle8888.h:43
const uint8_t tle8888_fwd_responses[16][4]
Definition tle8888.cpp:138
static const char * tle8888_pin_names[TLE8888_SIGNALS]
Definition tle8888.cpp:266
static brain_pin_diag_e tle8888_2b_to_diag_with_temp(unsigned int bits)
Definition tle8888.cpp:607
static THD_FUNCTION(tle8888_driver_thread, p)
Definition tle8888.cpp:815
void tle8888_dump_regs()
Definition tle8888.cpp:1279
int tle8888_add(brain_pin_e base, unsigned int index, const tle8888_config *cfg)
TLE8888 driver add.
Definition tle8888.cpp:1228
static Tle8888 chips[BOARD_TLE8888_COUNT]
Definition tle8888.cpp:264
void tle8888PostState()
Definition tle8888.cpp:279
void tle8888_req_init()
Definition tle8888.cpp:1272
static Timer diagResponse
Definition tle8888.cpp:42
tle8888_drv_state
Definition tle8888.cpp:54
@ TLE8888_WAIT_INIT
Definition tle8888.cpp:56
@ TLE8888_DISABLED
Definition tle8888.cpp:55
@ TLE8888_FAILED
Definition tle8888.cpp:58
@ TLE8888_READY
Definition tle8888.cpp:57
static brain_pin_diag_e tle8888_2b_to_diag_no_temp(unsigned int bits)
Definition tle8888.cpp:596