rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
tle9104.cpp
Go to the documentation of this file.
1#include "pch.h"
2#include "gpio/gpio_ext.h"
3#include "gpio/tle9104.h"
5
6#if defined(BOARD_TLE9104_COUNT) && BOARD_TLE9104_COUNT > 0
7
8/*
9 * TODO list:
10 * - support driving outputs over SPI
11 * - count communication errors, POR events, etc
12 * - support WD functionality
13 * - implement direct-io mapping test. Check if user provides correct direct_io data
14 */
15
16/*==========================================================================*/
17/* Driver local definitions. */
18/*==========================================================================*/
19
20#define DRIVER_NAME "tle9104"
21
22/* TODO: align with WD settings */
23#define TLE9104_POLL_INTERVAL_MS 100
24
25static bool drv_task_ready = false;
26
33
34/* IN MOSI and MISO a read is defined with a s0 and a write is defined with a 1. */
35#define TLE9104_WR_REG(addr, val) (BIT(15) | (((addr) & 0x0f) << 8) | ((val) & 0xff))
36#define TLE9104_RD_REG(addr) (((addr) & 0x0f) << 8)
37
38#define TLE9104_GET_VAL(rx) ((rx) & 0xff)
39#define TLE9104_GET_ADDR(rx) (((rx) >> 8) & 0x0f)
40
41#define TLE9104_PARITY_MASK(val) ((val) & (~BIT(14)))
42
43#define TLE9104_FAULT_GLOBAL BIT(12)
44#define TLE9104_FAULT_COMM BIT(13)
45
46#define TLE9104_REG_CTRL 0x00
47#define TLE9104_REG_CFG 0x01
48#define TLE9104_REG_OFF_DIAG_CFG 0x02
49#define TLE9104_REG_ON_DIAG_CFG 0x03
50#define TLE9104_REG_DIAG_OUT_1_2_ON 0x04
51#define TLE9104_REG_DIAG_OUT_3_4_ON 0x05
52#define TLE9104_REG_DIAG_OFF 0x06
53#define TLE9104_REG_GLOBAL_STATUS 0x07
54#define TLE9104_REG_ICVID 0x08
55
56/*==========================================================================*/
57/* Driver exported variables. */
58/*==========================================================================*/
59
60/*==========================================================================*/
61/* Driver local variables and types. */
62/*==========================================================================*/
63
64struct Tle9104 : public GpioChip {
65 int init() override;
66
67 int writePad(size_t pin, int value) override;
68 brain_pin_diag_e getDiag(size_t pin) override;
69 void debug() override;
70
71 int updateDiagState();
72 int updateStatus();
73
74 const tle9104_config* cfg;
75 tle9104_drv_state drv_state;
76private:
77 int spi_validate(uint16_t rx);
78 int spi_rw_array(const uint16_t *tx, uint16_t *rx, int n);
79 int spi_rw(uint16_t tx, uint16_t *rx);
80 int read_reg(uint8_t addr, uint8_t *val);
81 int write_reg(uint8_t addr, uint8_t val);
82
83 int chip_init();
84
85 OutputPin m_en;
86 OutputPin m_resn;
87
88 /* diagnostic registers */
89 uint8_t diag_off;
90 uint8_t diag_on12;
91 uint8_t diag_on34;
92
93 /* statistic */
94 int por_cnt;
95 int wdr_cnt;
96 int init_req_cnt;
97 int fault_cnt;
98 int fault_comm_cnt;
99 int spi_address_err_cnt;
100 int spi_cnt;
101 int spi_parity_err_cnt;
102};
103
104static Tle9104 chips[BOARD_TLE9104_COUNT];
105
106/*==========================================================================*/
107/* Driver local functions. */
108/*==========================================================================*/
109
110static bool parityBit(uint16_t val) {
111 // (1 + number of bits set) mod 2 = parity bit
112 int count = 1 + __builtin_popcount(val);
113
114 return count % 2;
115
116#if 0
117 while (val != 0) {
118 if (val & 0x01) {
119 count++;
120 }
121
122 val = val >> 1;
123 }
124
125 return (count % 2) == 1;
126#endif
127}
128
129int Tle9104::spi_validate(uint16_t rx) {
130 /* with parity bit included */
131 bool parityOk = !parityBit(rx);
132 if (!parityOk) {
133 spi_parity_err_cnt++;
134 return -53;
135 }
136
137 if (rx & TLE9104_FAULT_GLOBAL) {
138 fault_cnt++;
139 }
140
141 if (rx & TLE9104_FAULT_COMM) {
142 fault_comm_cnt++;
143 }
144
145 return 0;
146}
147
148int Tle9104::spi_rw(uint16_t tx, uint16_t *rx) {
149 SPIDriver *spi = cfg->spi_bus;
150
151 spi_cnt++;
152
153 // set the parity bit appropriately
154 tx |= parityBit(tx) << 14;
155
156 /* Acquire ownership of the bus. */
157 spiAcquireBus(spi);
158 /* Setup transfer parameters. */
159 spiStart(spi, &cfg->spi_config);
160 /* Slave Select assertion. */
161 spiSelect(spi);
162 /* Atomic transfer operations. */
163 uint16_t rxd = spiPolledExchange(spi, tx);
164 /* Slave Select de-assertion. */
165 spiUnselect(spi);
166 /* Ownership release. */
167 spiReleaseBus(spi);
168
169 /* with parity bit included */
170 int ret = spi_validate(rxd);
171 if (ret < 0) {
172 return ret;
173 }
174
175 /* TODO: check Fault Global and Fault Communication flags */
176
177 // return data
178 if (rx) {
179 *rx = rxd;
180 }
181
182 return 0;
183}
184
185int Tle9104::spi_rw_array(const uint16_t *tx, uint16_t *rx, int n)
186{
187 int ret = 0;
188 SPIDriver *spi = cfg->spi_bus;
189
190 if ((n <= 0) || (tx == nullptr)) {
191 return -2;
192 }
193
194 /* Acquire ownership of the bus. */
195 spiAcquireBus(spi);
196 /* Setup transfer parameters. */
197 spiStart(spi, &cfg->spi_config);
198
199 for (int i = 0; i < n; i++) {
200 spi_cnt++;
201
202 /* Slave Select assertion. */
203 spiSelect(spi);
204 /* data transfer */
205 uint16_t rxdata = spiPolledExchange(spi, tx[i]);
206
207 if (rx)
208 rx[i] = rxdata;
209 /* Slave Select de-assertion. */
210 spiUnselect(spi);
211
212 /* validate reply */
213 ret = spi_validate(rxdata);
214 if (ret < 0)
215 break;
216
217 if (i >= 1) {
218 /* validate that we received correct answer to previous tx */
219 if (TLE9104_GET_ADDR(tx[i - 1] != TLE9104_GET_ADDR(rxdata))) {
220 spi_address_err_cnt++;
221 ret = -2;
222 break;
223 }
224 }
225 }
226 /* Ownership release. */
227 spiReleaseBus(spi);
228
229 /* no errors for now */
230 return ret;
231}
232
233int Tle9104::read_reg(uint8_t addr, uint8_t *val) {
234 int ret;
235
236 ret = spi_rw(TLE9104_RD_REG(addr), nullptr);
237 if (ret) {
238 return ret;
239 }
240
241 uint16_t rxd;
242 ret = spi_rw(TLE9104_RD_REG(addr), &rxd);
243 if (ret) {
244 return ret;
245 }
246
247 if (val) {
248 *val = TLE9104_GET_VAL(rxd);
249 }
250
251 return 0;
252}
253
254int Tle9104::write_reg(uint8_t addr, uint8_t val) {
255 // R/W bit is 1 for write
256 return spi_rw(TLE9104_WR_REG(addr, val), nullptr);
257}
258
259int Tle9104::chip_init() {
260 int ret;
261
262 init_req_cnt++;
263
264 // disable comms watchdog, enable direct drive on all 4 channels
265 // TODO: should we enable comms watchdog?
266 ret = write_reg(TLE9104_REG_CFG, 0x0F);
267 if (ret) {
268 return ret;
269 }
270
271 // clear any suprious diag states from startup: first call resets, second reads true state
272 ret = updateDiagState();
273 if (ret) {
274 return ret;
275 }
276 ret = updateDiagState();
277 if (ret) {
278 return ret;
279 }
280
281 // set output enable, clear all other flags
282 ret = write_reg(TLE9104_REG_GLOBAL_STATUS, BIT(7));
283 if (ret) {
284 return ret;
285 }
286
287 drv_state = TLE9104_READY;
288
289 return 0;
290}
291
292/*==========================================================================*/
293/* Driver thread. */
294/*==========================================================================*/
295
296SEMAPHORE_DECL(tle9104_wake, 10 /* or BOARD_TLE9104_COUNT ? */);
297static THD_WORKING_AREA(tle9104_thread_1_wa, 256);
298
300
301static THD_FUNCTION(tle9104_driver_thread, p) {
302 int i;
303 msg_t msg;
304
305 (void)p;
306
307 chRegSetThreadName(DRIVER_NAME);
308
309 while(1) {
310 if (!gatekeeper.haveVoltage()) {
311 chThdSleepMilliseconds(300);
312 return;
313 }
314
315 msg = chSemWaitTimeout(&tle9104_wake, TIME_MS2I(TLE9104_POLL_INTERVAL_MS));
316
317 /* should we care about msg == MSG_TIMEOUT? */
318 (void)msg;
319
320 for (i = 0; i < BOARD_TLE9104_COUNT; i++) {
321 int ret;
322 Tle9104& chip = chips[i];
323
324 if (!chip.cfg ||
325 (chip.drv_state == TLE9104_DISABLED) ||
326 (chip.drv_state == TLE9104_FAILED))
327 continue;
328
329 ret = chip.updateDiagState();
330 if (ret) {
331 /* set state to TLE6240_FAILED? */
332 }
333 ret = chip.updateStatus();
334 if (ret) {
335 /* set state to TLE6240_FAILED? */
336 }
337 }
338 }
339}
340
341/*==========================================================================*/
342/* Driver exported functions. */
343/*==========================================================================*/
344
345int Tle9104::writePad(size_t pin, int value) {
346 auto port = cfg->direct_io[pin].port;
347 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, port != nullptr, "unused 9104 port", -1);
348
349 // Inverted since TLE9104 is active low (low level to turn on output)
350 if (value) {
351 palClearPad(port, cfg->direct_io[pin].pad);
352 } else {
353 palSetPad(port, cfg->direct_io[pin].pad);
354 }
355
356 return 0;
357}
358
359int Tle9104::updateDiagState() {
360 int ret;
361
362 /* TODO: implement and use spi_rw_array() */
363 ret = read_reg(TLE9104_REG_DIAG_OUT_1_2_ON, &diag_on12);
364 if (ret) {
365 return ret;
366 }
367 ret = read_reg(TLE9104_REG_DIAG_OUT_3_4_ON, &diag_on34);
368 if (ret) {
369 return ret;
370 }
371 ret = read_reg(TLE9104_REG_DIAG_OFF, &diag_off);
372 if (ret) {
373 return ret;
374 }
375
376 // clear diag states
377 ret = write_reg(TLE9104_REG_DIAG_OUT_1_2_ON, 0);
378 if (ret) {
379 return ret;
380 }
381 ret = write_reg(TLE9104_REG_DIAG_OUT_3_4_ON, 0);
382 if (ret) {
383 return ret;
384 }
385 ret = write_reg(TLE9104_REG_DIAG_OFF, 0);
386 if (ret) {
387 return ret;
388 }
389
390 return 0;
391}
392
393int Tle9104::updateStatus() {
394 int ret;
395
396 uint8_t status;
397 ret = read_reg(TLE9104_REG_GLOBAL_STATUS, &status);
398 if (ret) {
399 return ret;
400 }
401
402 /* Device was reset since last cleared */
403 if (status & BIT(0)) {
404 por_cnt++;
405 need_init = true;
406 }
407 /* Communication watchdog timeout occurred */
408 if (status & BIT(2)) {
409 wdr_cnt++;
410 need_init = true;
411 }
412
413 if (need_init) {
414 ret = chip_init();
415 if (ret == 0) {
416 need_init = false;
417 }
418 }
419
420 return 0;
421}
422
423brain_pin_diag_e Tle9104::getDiag(size_t pin) {
424 uint8_t off_diag;
425 uint8_t on_diag;
426
427 switch (pin) {
428 case 0:
429 on_diag = diag_on12;
430 off_diag = diag_off;
431 break;
432 case 1:
433 on_diag = diag_on12 >> 3;
434 off_diag = diag_off >> 2;
435 break;
436 case 2:
437 on_diag = diag_on34;
438 off_diag = diag_off >> 4;
439 break;
440 case 3:
441 on_diag = diag_on34 >> 3;
442 off_diag = diag_off >> 6;
443 break;
444 default:
445 return PIN_UNKNOWN;
446 }
447
448 // on diag has 3 bits
449 on_diag = on_diag & 0x7;
450 // of diag has 2 bits
451 off_diag = off_diag & 0x3;
452
453 int result = 0;
454
455 // Decode on-state faults
456 switch (on_diag) {
457 case 2:
458 result |= PIN_SHORT_TO_BAT;
459 break;
460 case 3:
461 // overtemp and overcurrent
462 result |= PIN_DRIVER_OVERTEMP;
463 [[fallthrough]];
464 case 4:
465 result |= PIN_OVERLOAD;
466 break;
467 case 5:
468 result |= PIN_DRIVER_OVERTEMP;
469 break;
470 }
471
472 // Decode off-state faults
473 switch (off_diag) {
474 case 2:
475 result |= PIN_OPEN;
476 break;
477 case 3:
478 result |= PIN_SHORT_TO_GND;
479 break;
480 }
481
482 return (brain_pin_diag_e)result;
483}
484
485void Tle9104::debug() {
486 efiPrintf("spi transfers %d with patiry error %d with wrong address %d communication fault counter %d\n",
487 spi_cnt, spi_parity_err_cnt, spi_address_err_cnt, fault_comm_cnt);
488 efiPrintf("fault counter %d communication fault counter %d\n",
489 fault_cnt, fault_comm_cnt);
490 efiPrintf("POR counter %d reinit counter %d WD counter %d\n",
491 por_cnt, init_req_cnt, wdr_cnt);
492}
493
494int Tle9104::init() {
495 int ret;
496
497 m_resn.initPin(DRIVER_NAME " RESN", cfg->resn);
498 m_en.initPin(DRIVER_NAME " EN", cfg->en);
499
500 // disable outputs
501 m_en.setValue(false);
502 // Reset the chip
503 m_resn.setValue(false);
504
505 /* TODO: ensure all direct_io pins valid, otherwise support manipulationg output states over SPI */
506 for (int i = 0; i < TLE9204_OUT_COUNT; i++) {
507 auto port = cfg->direct_io[i].port;
508 if (port == nullptr) {
509 // skipping unused io
510 continue;
511 }
512 gpio_pin_markUsed(port, cfg->direct_io[i].pad, DRIVER_NAME " Direct IO");
513 palSetPadMode(cfg->direct_io[i].port, cfg->direct_io[i].pad, PAL_MODE_OUTPUT_PUSHPULL);
514
515 // Ensure all outputs are off
516 writePad(i, false);
517 }
518
519 if (isBrainPinValid(cfg->resn)) {
520 chThdSleepMilliseconds(1);
521 m_resn.setValue(true);
522 chThdSleepMilliseconds(1);
523 }
524
525 // read ID register
526 uint8_t id;
527 ret = read_reg(TLE9104_REG_ICVID, &id);
528
529 if (ret) {
530 return ret;
531 }
532 // No chip detected if ID is wrong
533 if (id != 0xB1) {
534 return -54;
535 }
536
537 ret = chip_init();
538 if (ret) {
539 return ret;
540 }
541
542 // Set hardware enable
543 m_en.setValue(true);
544
545 if (!drv_task_ready) {
546 chThdCreateStatic(tle9104_thread_1_wa, sizeof(tle9104_thread_1_wa),
547 PRIO_GPIOCHIP, tle9104_driver_thread, nullptr);
548 drv_task_ready = true;
549 }
550
551 return 0;
552}
553
554int tle9104_add(Gpio base, int index, const tle9104_config* cfg) {
555 Tle9104& chip = chips[index];
556
557 /* already added? */
558 if (chip.cfg != nullptr) {
559 return -52;
560 }
561
562 chip.cfg = cfg;
563 chip.drv_state = TLE9104_WAIT_INIT;
564
565 return gpiochip_register(base, DRIVER_NAME, chip, 4);
566}
567
568void initAll9104(const tle9104_config *configs) {
569 for (int chipIndex = 0;chipIndex < BOARD_TLE9104_COUNT;chipIndex++) {
570 int ret = tle9104_add((Gpio)(Gpio::TLE9104_0_OUT_0 + TLE9204_OUT_COUNT * chipIndex), chipIndex, &configs[chipIndex]);
571 if (ret < 0) {
572 criticalError("tle9104_add");
573 }
574 }
575}
576
577#else // BOARD_TLE9104_COUNT > 0
578
579int tle9104_add(Gpio, int, const tle9104_config*) {
580 return -222;
581}
582
583#endif // BOARD_TLE9104_COUNT
constexpr uint8_t addr
Definition ads1015.cpp:14
Single output pin reference and state.
Definition efi_output.h:49
@ TLE9104_0_OUT_0
int gpiochip_register(brain_pin_e base, const char *name, GpioChip &gpioChip, size_t size)
Register gpiochip.
Definition core.cpp:186
void writePad(const char *msg, brain_pin_e pin, int bit)
Definition io_pins.cpp:118
@ CUSTOM_ERR_ASSERT
bool isBrainPinValid(brain_pin_e brainPin)
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
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 init()=0
virtual void debug()
Definition gpio_ext.h:33
static Tle9104 chips[BOARD_TLE9104_COUNT]
Definition tle9104.cpp:104
SEMAPHORE_DECL(tle9104_wake, 10)
static bool drv_task_ready
Definition tle9104.cpp:25
void initAll9104(const tle9104_config *configs)
Definition tle9104.cpp:568
static THD_WORKING_AREA(tle9104_thread_1_wa, 256)
static bool parityBit(uint16_t val)
Definition tle9104.cpp:110
static THD_FUNCTION(tle9104_driver_thread, p)
Definition tle9104.cpp:301
tle9104_drv_state
Definition tle9104.cpp:27
@ TLE9104_FAILED
Definition tle9104.cpp:31
@ TLE9104_WAIT_INIT
Definition tle9104.cpp:29
@ TLE9104_READY
Definition tle9104.cpp:30
@ TLE9104_DISABLED
Definition tle9104.cpp:28
static IgnVoltageGatekeeper gatekeeper
Definition tle9104.cpp:299
int tle9104_add(Gpio base, int index, const tle9104_config *cfg)
Definition tle9104.cpp:554
uint16_t count
Definition tunerstudio.h:1